String的不可变性由final类、final字符数组和无修改方法三层机制保障,使intern能安全实现引用归一化;调用intern时JVM查池并登记引用,确保内容相同字符串共享同一实例。

String 的不可变性不是“不能改内容”的表面说法,而是由类设计、内存结构和 JVM 协同保障的一套机制;intern 方法正是在这个基础上,通过字符串常量池(String Pool)实现引用归一化的关键手段。
不可变性的三层硬约束
它不是靠程序员自觉遵守,而是从语言层强制锁定:
- 类被 final 修饰:String 类无法被继承,杜绝子类绕过限制增加修改能力
-
内部字符数组被 final 修饰:JDK 8 及之前是
private final char[] value,JDK 9+ 改为private final byte[] value+ 编码标识,但引用本身不可重赋值 -
无公开的修改方法:所有看似“修改”的方法(如
substring、replace、toUpperCase)都返回新对象,原对象保持原样
为什么必须不可变,intern 才能安全工作
如果 String 可变,常量池就失去意义——两个变量引用同一个池中对象,一个改了内容,另一个读到的就是脏数据。而不可变性让 JVM 敢于做三件事:
- 把相同字面量(如
"abc")指向同一块内存 - 缓存
hashCode()值,避免重复计算(String 常作 HashMap 键) - 允许
intern()在运行时“认领”堆中字符串,并保证后续对该字符串的所有访问都复用池中唯一实例
intern 方法的底层执行逻辑
调用 str.intern() 时,JVM 实际在做一次“池内查重 + 条件入驻”:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 检查字符串常量池中是否存在内容完全相等(
equals()语义)的字符串 - 若存在,直接返回池中已有对象的引用
- 若不存在,将当前字符串对象的引用(注意:不是复制内容,而是登记该对象)加入常量池,并返回该引用
特别注意:从 Java 7 开始,字符串常量池已移到堆内存中,不再是永久代(PermGen),所以 intern() 不再容易引发 PermGen OOM;但频繁调用仍可能增加 GC 压力,需结合业务权衡。
常见误区与验证要点
很多困惑源于混淆了“内容相等”和“引用相等”,用几个典型例子就能厘清:
-
"hello" == "hello"→ true(字面量自动入池,共享引用) -
new String("hello") == new String("hello")→ false(堆中两个独立对象) -
new String("hello").intern() == "hello"→ true(前者入池后,与字面量指向同一对象) -
new String("hello").intern() == new String("hello").intern()→ true(两次调用都命中池中同一实例)
本质上,intern() 不是在“创建新字符串”,而是在为已有字符串“分配一个全局唯一身份证”,这个身份证就是常量池里的引用。

















