Java字符串不可变指其内容在创建后无法修改,所有操作均返回新对象;真正保障在于无公开修改接口、String类为final,而非仅靠final数组;这支撑常量池、哈希缓存和线程安全。

Java 里的字符串不可变,意思是:一旦一个 String 对象被创建出来,它的内容(即所表示的字符序列)在对象整个生命周期中就固定不变了。你无法通过任何合法、常规的方式去修改它内部的值——所有看似“修改”的操作,比如拼接、截取、转大写等,其实都返回了一个**全新的 String 对象**,而原对象毫发无损,依然留在内存里。
不可变性不是靠“final 数组” alone 实现的
很多人看到 private final char[] value 就以为“final 数组 = 内容不可改”,这是常见误解。实际上:
-
final只锁住数组的引用,不锁住数组元素(比如value[0] = 'x'在反射下仍可能成功) - 真正保障不可变的是:没有公开的 setter 方法、没有暴露
value数组的访问途径、所有构造逻辑都确保数组只读使用 - String 类本身是
final的,杜绝子类篡改行为或重写关键方法
为什么必须不可变?三大硬需求
不可变不是为了炫技,而是支撑 Java 核心机制的底层基石:
-
字符串常量池能正常工作:比如
String a = "abc"; String b = "abc";,JVM 靠“内容绝对不变”才敢让a和b共享同一个对象;否则一个改了,另一个就“中毒”了 -
哈希码可缓存且稳定:String 经常作 HashMap 的 key。因为内容永不变化,它的
hashCode()值在第一次调用后就能安全缓存(private int hash),后续直接复用,不用重复计算 - 天然线程安全:多个线程同时读同一个 String,完全无需同步;不会出现“读到一半被其他线程改掉”的竞态问题
不可变带来的实际影响
写代码时,这个特性会直接影响你的判断和选择:
立即学习“Java免费学习笔记(深入)”;
- 频繁拼接字符串(如循环内
s += "x")会不断创建新对象,造成内存压力和 GC 开销 → 应改用StringBuilder -
==比较只对字面量或显式intern()后的字符串有意义;一般场景必须用.equals() - 传参时不用担心方法内部篡改你的字符串(比如密码、SQL 片段、配置项),安全性有保障
- 虽然不能改,但你可以放心地把它作为
final字段、Map 的 key、并发共享状态,逻辑更清晰可靠
小结:不可变 ≠ 不能换引用,而是内容锁死
变量可以重新赋值(str = str + "!"),但这只是让变量指向新对象;原来那个 "Hello" 还在堆里,没被动过。这种设计用空间换确定性,换来的是安全、性能和简洁——不是限制你,而是替你挡住一堆潜在陷阱。










