Java String不可变性是intern()安全管理常量池的根本前提:String被声明为final,内部byte[](JDK9+)或char[]为final引用,所有修改操作均返回新对象,确保常量池中字符串内容恒定、线程安全。

Java String 的不可变性是 intern() 能安全管理常量池的根本前提——没有不可变性,共享引用就会引发数据污染和线程安全问题。
不可变性为 intern 提供安全基础
String 类被声明为 final,内部字符数组(JDK 9+ 是 byte[])也是 final 引用,且所有修改方法(如 substring、replace、toUpperCase)都返回新对象。这意味着:
- 一旦某个字符串进入常量池,它的内容就永远固定,不会被任何代码意外修改
- 多个变量(如
s1 = "OK"、s2 = "OK")可安全指向池中同一实例,即使并发访问也不会相互干扰 - 若 String 可变,一个线程调用
s1.toUpperCase()(假设能原地改),另一个线程正用s2作为HashMap的 key,就可能读到被篡改的值
intern() 如何依赖并强化这一机制
intern() 不是“让字符串变不可变”,而是“在不可变前提下,统一归集相同内容的字符串引用”:
- 调用
str.intern()时,JVM 检查常量池中是否存在内容相等(equals)的字符串 - 若存在,直接返回池中那个已有的、不可变的实例引用
- 若不存在,将当前字符串(或其副本,JDK 7+ 直接存堆中该对象引用)加入池,并返回该引用
- 后续所有相同内容的
intern()调用,都会收敛到同一个不可变对象上
哪些字符串适合 intern?关键看生命周期和重复率
不可变性让 intern 安全,但不等于所有字符串都该 intern。是否值得,取决于实际场景:
- ✅ 推荐:日志级别("INFO"、"ERROR")、HTTP 方法("GET"、"POST")、状态码("SUCCESS"、"PENDING")、配置项名——这些字符串内容固定、重复极高、存活时间长
- ❌ 避免:UUID、毫秒级时间戳、用户输入的随机昵称、临时拼接路径——几乎不重复,intern 后白占堆内存,且无法被 GC 即时回收
- ⚠️ 注意:字面量(
"hello")和final拼接结果已自动入池,无需再调用intern()
使用 intern 的典型模式
对非字面量来源的重复字符串,显式调用 intern() 是最直接的优化方式:
- 从 JSON 或数据库读取字段值:
String status = json.get("status").intern(); - 解析协议头:
String method = parseMethod(line).intern(); - 避免在循环内无条件调用——先判断是否真有重复,或用
ConcurrentHashMap显式缓存更可控 - 验证效果需借助工具:对比 JFR 或 MAT 中
java.lang.String实例数与 retained heap,而非仅看代码写了 intern
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











