Java中String的不可变性通过四层机制实现:final类与private final字段阻断继承和引用替换;构造时防御性拷贝隔绝外部污染;所有“修改”方法均返回新对象而不改变原状态;且无任何公开修改接口。

Java 中 String 的不可变性不是靠“禁止修改”这句口号实现的,而是通过三层具体机制协同封堵所有可能篡改内部状态的路径,让对象从创建起就“锁死”内容。
final 类 + private final 字段:切断继承与引用替换
String 被声明为 public final class,无法被继承——这意味着没人能写个子类,偷偷加个 setCharAt() 或覆盖 toString() 来绕过防护。它的核心数据字段(JDK 8 是 private final char[] value,JDK 9+ 是 private final byte[] value)同时满足两个硬约束:
- private:外部代码连数组都看不见,更别说读或写
- final:该字段初始化后,永远指向同一块内存,不能被重新赋值为另一个数组
构造时防御性拷贝:隔绝外部输入污染
哪怕你传入一个可变的 char[] 给 String 构造器(比如 new String(charArray)),它也不会直接引用你传进来的数组,而是调用 Arrays.copyOf() 创建一份新副本。这样,即使你后续修改原数组,String 对象里的 value 内容丝毫不受影响。
例如:
立即学习“Java免费学习笔记(深入)”;
char[] arr = {'h', 'e', 'l', 'l', 'o'};String s = new String(arr);
arr[0] = 'H'; // 修改原数组
System.out.println(s); // 仍输出 "hello",不受影响
所有“修改”方法都返回新对象:不碰原状态一根手指
replace、substring、toUpperCase、concat、trim……这些方法看起来像在改字符串,其实全部是“只读+新建”模式:
- 读取原 value 数组的内容
- 按需复制、截取或转换,生成新的字符数组
- 用这个新数组构造并返回一个全新的 String 实例
- 原对象的 value 字段和内容,全程未被任何 public API 触碰
变量重新赋值(如 s = s + "!")只是让引用指向新对象,原 String 依然静静躺在堆里或常量池中,内容分毫不变。
无任何公开修改接口:从设计上不留后门
String 类不提供任何 setter、append、insert、setCharAt 等会改变自身状态的方法。它甚至没有暴露 value 数组的 getter(value() 方法是 package-private,且返回的是原始数组引用的副本,JDK 9+ 还加了访问限制)。这种“封闭式封装”配合前面三重防护,使得在标准 Java 语义下,没有任何合法途径能变更一个已创建 String 对象的内部状态。


















