java string不可变性由final字段、私有数组及无修改方法共同保障,支撑常量池复用、线程安全与哈希缓存,并驱动jdk9紧凑字符串等jvm优化。

Java 中 String 对象的内存结构和引用指向,核心在于“不可变性”与“池化机制”的协同作用。理解这一点,就能理清为什么 s1 == s2 有时为 true,有时为 false,以及为什么修改字符串实际是创建新对象。
字符串常量池:字面量的归属地
所有用双引号直接写的字符串(如 "hello")都优先存入字符串常量池——它位于堆内存中(JDK 7+),不是方法区。JVM 会检查池中是否已有相同内容:
- 有,则复用该地址,多个变量指向同一块内存;
- 没有,则在池中新建字符串,并返回其地址。
例如:String s1 = "abc";String s2 = "abc";
此时 s1 和 s2 的引用值完全相同,s1 == s2 返回 true。
堆内存中的 new 对象:独立副本
使用 new String("abc") 时,JVM 会在堆中开辟全新空间,即使内容与常量池中一致:
- 先确保常量池存在
"abc"(若无则创建); - 再在堆中新建一个
String实例,其内部value字段指向常量池中的字节数组(JDK 9+)或拷贝一份(JDK 8-); - 栈中变量存储的是这个堆对象的地址,与常量池地址不同。
所以:String s1 = "abc";String s2 = new String("abc");s1 == s2 为 false(地址不同),但 s1.equals(s2) 为 true(内容相同)。
底层存储:从 char[] 到 byte[] 的演进
String 的不可变性由其底层字段保障:
- JDK 8 及以前:
private final char value[]—— 每个字符占 2 字节(UTF-16); - JDK 9 起:
private final byte value[]+private final byte coder—— 根据内容自动选择 LATIN1(单字节)或 UTF16(双字节),节省内存; -
final修饰保证数组引用不可重赋,private阻止外部篡改,共同实现“逻辑不可变”。
注意:不可变指内容不能被修改,不是说内存地址不能变——变量本身可以重新赋值,比如 s1 = "xyz",只是让 s1 指向另一个字符串对象。
引用指向的本质:栈中存地址,地址连向真实数据
声明 String s = "abc" 时:
- 栈中变量
s存储的是一个内存地址; - 该地址指向常量池中
"abc"对应的byte[]数据块; - 任何对
s的“修改”(如s += "d")都会生成新字符串对象,s改为指向新地址,原对象不受影响。
这种设计让字符串天然适合缓存、哈希计算(hash 字段可缓存)、线程安全,也解释了为何频繁拼接推荐用 StringBuilder——避免大量临时对象。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











