String不可变是通过final类、private final字段及JVM协同强制实现的,确保字符序列、哈希值和value数组引用均不可修改,支撑常量池、线程安全与类加载信任机制。

Java 里的 String 不是“被设计成不可变”,而是**通过语言机制+类定义+JVM配合,强制实现不可变**——一旦创建,其字符序列、哈希值、甚至内部 value 数组引用都不可对外修改。
为什么 String 类被声明为 final
这是不可变性的第一道防线:
-
final类不能被继承,避免子类重写方法(比如substring()在 Java 7u6 之前返回共享数组,若可继承就可能被绕过) - 防止通过多态注入可变逻辑,例如伪造一个“可变 String”并混入
Map的 key 中 - JVM 可基于
final做更多优化(如字符串常量池的 intern 判定、内联假设)
value 字段为何是 private final char[](或 byte[])
从 Java 9 开始,String 内部改用 private final byte[] value + coder 字段存储,但关键点没变:
-
private封装了底层数据,外部无法直接访问或修改value -
final保证该数组引用不可被重新赋值(注意:它不阻止数组元素被改——但 JDK 从未提供任何修改元素的 public 方法) - 所有构造和操作方法(
concat()、replace()、substring())都返回新对象,而非复用原value
不可变性如何影响字符串常量池和 == 比较
常量池依赖不可变性才能安全复用对象:
立即学习“Java免费学习笔记(深入)”;
- 如果
"abc"被两个变量引用,且其中一个能改内容,另一个会“意外”看到变化——这违背直觉且破坏线程安全 -
==比较在常量池中有效,正是因为编译期字面量和intern()结果必然指向同一不可变实例 - 若 String 可变,JVM 就无法在运行时缓存
hash值(目前hash是private int hash+ 懒计算),每次hashCode()都得重算,严重拖慢HashMap性能
常见误解:new String("abc") 创建了几个对象?
这个问题容易误导人,重点不在“几个对象”,而在“是否共享底层数据”:
-
new String("abc")一定新建堆上对象,但其value数组可能与常量池中"abc"共享(Java 8 及以前),也可能复制(Java 9+ 默认复制,除非明确用new String("abc", coder)) - 真正关键的是:无论是否共享数组,你都无法通过任何 public API 修改它——因为没有
setCharAt()或setValue()方法 - 有人用反射强行改
value数组内容,那属于破坏封装的 hack 行为,不属于 String 设计契约范畴
不可变性的代价是频繁拼接产生大量中间对象,所以需要 StringBuilder;它的价值不是“看起来安全”,而是让哈希计算、线程共享、类加载器隔离这些底层机制得以成立。最易被忽略的一点:String 的不可变性,是 JVM 实现类加载双亲委派和模块系统信任边界的基础之一。








