String不可变指对象内容创建后无法更改,仅能新建对象;通过final类、private final字段、无写入接口及防御性拷贝三重机制实现;带来线程安全、哈希缓存、安全校验和常量池优化四大价值。

要真正掌握 String 不可变性,不能只背“final 类 + final 数组”这句结论。关键在于理解它“为什么不可变”“怎么做到不可变”“不可变带来了什么”,以及“哪些操作看似修改实则新建”。下面从四个实战维度帮你打通任督二脉。
String 不可变到底指什么
不是变量不能重新赋值,而是对象内容一旦创建就无法更改。比如:
- s = "abc"; s = "def"; —— 这是让引用 s 指向新对象,原 "abc" 对象没变
- s.replace('a', 'x'); —— 返回新字符串 "xbc",s 本身仍指向 "abc"
- 真正的修改必须显式赋值:s = s.replace('a', 'x');
类比合同原件:你不能涂改,只能签新合同。旧合同还在那里,直到没人引用才被回收。
底层三重防护机制
不可变不是靠一个 final 实现的,而是三层设计共同保障:
立即学习“Java免费学习笔记(深入)”;
- 类 final:String 不能被继承,杜绝子类覆盖方法破坏行为
- 字段 private final:value 数组既不可被外部访问(private),引用又不可重定向(final)
-
无写入接口 + 防御性拷贝:构造时用
Arrays.copyOf()复制传入数组;所有“修改”方法(concat、substring 等)都返回新 String,不提供任何 setXXX 方法
注意:JDK 9+ 改用 byte[] value + coder 字段优化内存,但不可变内核完全一致。
常量池与 intern 是不可变的直接产物
正因为 String 不可变,JVM 才敢在字符串常量池中复用同一份数据:
- "hello" == "hello" → true(字面量自动入池,地址相同)
- new String("hello") == "hello" → false(堆上新建对象,与池中不同)
- new String("hello").intern() == "hello" → true(手动入池后指向池中对象)
如果 String 可变,两个变量共用一个池中对象,一个改了内容,另一个就“意外”变了——这是灾难性的。
不可变带来的四大实际价值
面试官想听的不只是“怎么实现”,更是“为什么这么设计”:
- 线程安全:无需同步,多线程共享同一个 String 安全可靠
-
哈希缓存有效:hash 值可计算一次并长期缓存(
private int hash),提升 HashMap 等集合性能 - 安全校验可信:如数据库用户名传入方法后被篡改?不可能——传进去的是不可变副本
- JVM 优化基础:常量池、字符串拼接编译期优化(如 "a"+"b"→"ab")、运行时去重都依赖不可变前提
顺带一提:需要频繁修改时,该用 StringBuilder 或 StringBuffer,它们才是为“可变”场景设计的。


















