Java中只有编译期字面量、final拼接结果及显式调用intern()的字符串才进入常量池;其重用安全依赖String不可变性,确保多引用共享不引发数据污染;对长生命周期重复字符串可主动intern()以节省内存,但需避免对短命或唯一字符串滥用。

Java中String对象的缓存机制,核心是字符串常量池(String Pool),它依赖String不可变性实现高效重用。不是所有String都被自动缓存,只有满足特定条件的对象才会进入池中并被复用。
哪些字符串会进常量池?
编译期确定的字面量和显式调用intern()的字符串才可能进入常量池:
-
字面量直接赋值:如
String s = "hello";,JVM在类加载阶段就将该字符串放入常量池 -
final修饰的字符串拼接:如
final String a = "he"; final String b = "llo"; String c = a + b;,编译器能识别为常量表达式,结果也入池 -
运行期调用intern():如
new String("world").intern(),若池中尚无"world",则把该字符串引用(Java 7+)或副本(Java 6)加入池 -
静态初始化块中的字面量也会被纳入常量池管理,但动态构造(如
new String("abc"))默认不入池
常量池如何保证重用安全?
不可变性是重用的前提——池中每个字符串内容永远不会改变,所以多个引用共享同一对象不会引发数据污染:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 当执行
String a = "test"; String b = "test";时,a == b为true,因为两者都指向池中唯一实例 - 如果String可变,线程A调用
a.toUpperCase()(假设能原地修改),线程B正在用b做Map键,就会读到意外的大写内容 - JVM无需加锁即可复用池中对象,既省内存又免同步开销
什么时候该主动调用intern()?
对重复出现、生命周期长、用作key或标识符的字符串,intern()可显著降低堆内存占用:
立即学习“Java免费学习笔记(深入)”;
- 从数据库查出大量相同用户名、状态码、配置项等字符串时,
str.intern()能让它们指向池中统一实例 - switch语句中使用字符串作为case值,JVM内部已自动调用
intern()优化匹配效率 - 注意权衡:频繁调用
intern()会增加字符串表查找开销;池过大也可能影响GC(尤其老版本JDK) - 避免对短生命周期或唯一性字符串(如UUID生成的字符串)调用
intern(),得不偿失
常见误区与验证技巧
别混淆“对象创建”和“引用复用”,用==和equals()对比最直观:
-
String a = "ab"; String b = "ab";→a == b为true -
String c = new String("ab");→a == c为false,但a.equals(c)为true -
c.intern() == a在Java 7+中为true,说明intern()成功复用了池中对象 - 用
java -XX:+PrintStringTableStatistics可查看当前常量池大小和哈希桶分布,辅助调优

















