java中string的不可变性由三方面协同实现:final类禁止继承破坏契约;private final char[] value切断外部访问且引用不可变;所有操作方法均不修改原对象而是返回新实例。

Java 中 String 的不可变性(Immutability)**不是仅靠 final 类和私有数组就自动实现的**,而是由三者协同、配合严格的设计约束共同达成的:类被声明为 final、内部字符数组被声明为 private final、且所有可能修改状态的方法都**不暴露可变入口、不修改原数组、而是返回新对象**。
final 类:防止子类破坏不可变契约
public final class String 意味着无法继承 String。这很关键——如果允许子类存在,子类就可能:
- 添加可修改内部
value数组的方法; - 重写已有方法,偷偷改变内部状态;
- 通过反射绕过访问限制(虽不推荐,但子类可封装更危险逻辑)。
加 final 就从语言层面封死了“通过继承扩展可变行为”的路径,保证所有 String 实例都严格遵循同一套不可变实现。
private final char[] value:切断外部直接访问与内部重用
在 JDK 8 及之前,String 内部用的是:
private final char value[];
两个修饰符各司其职:
-
private:外部代码无法直接读写该数组,连反射默认也受限(需setAccessible(true),且现代 JDK 还有模块系统进一步限制); -
final:数组引用本身不可被重新赋值(比如不能执行this.value = new char[10]),确保对象创建后指向的数组内存地址不变。
⚠️ 注意:final char[] **只保证数组引用不变,不保证数组元素不可变**(即仍可通过 value[i] = 'x' 修改内容)。所以仅靠这个还不够——真正阻止修改的是:String 类**根本不提供任何修改 value 元素的 public 方法**,且所有构造和操作逻辑都避免原地更改。
无状态修改方法 + 新对象返回:不可变性的行为保障
这是最核心的一环。String 的所有看似“修改”的方法,如 substring()、toUpperCase()、replace()、concat() 等,全部:
- 不修改当前对象的
value数组; - 不复用原数组(JDK 7u6 之后已修复 substring 内存泄漏问题,改用拷贝);
- 而是基于原内容**新建
char[](或使用不可变视图)并构造新的String实例**。
例如:
String s = "hello";
String t = s.toUpperCase(); // 返回新 String,s.value 未被 touch
即使内部用 Arrays.copyOfRange 或 new String(value, offset, count),也都确保原始 s 的状态彻底冻结。
额外加固:构造阶段防御与字段冻结
String 构造器会主动防御外部传入的可变数组:
- 对
char[]参数,会做Arrays.copyOf拷贝(而非直接引用),防止调用方后续修改传入数组影响字符串内容; - JDK 9+ 使用
byte[] value+coder字段优化内存,但同样用private final+ 拷贝策略 + 无修改 API 保持语义不可变; - 所有字段(
hash、hash32等缓存)也设计为惰性计算、线程安全且不影响逻辑状态。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











