String.intern() 到底干了什么

String.intern() 是 JDK 6 开始有的方法,面试经常被问到,但很多人只停留在"字符串驻留"这个概念上,具体怎么回事不太清楚。

一、intern 的基本行为

调用 intern() 后,如果字符串池里已经有一个等于此对象的字符串,直接返回池里的那个;如果没有,把此对象加入字符串池,返回池里的引用。

java 复制代码
String s1 = new String("abc");
String s2 = s1.intern();

System.out.println(s1 == s2);  // false:s1 是堆对象,s2 是池里的

但 intern 用不好容易踩坑。

二、一个经典面试题

java 复制代码
String s1 = new String("a") + new String("b");
String s2 = s1.intern();

String s3 = new String("ab");
System.out.println(s1 == s2);  // true 还是 false?

先说答案:JDK 6 是 false,JDK 7+ 是 true。

解释一下执行过程:

JDK 6:

  • s1 = new String("a") + new String("b") 在堆里创建两个对象 "a" "b",拼接后生成 "ab",s1 指向堆
  • s2 = s1.intern() 把 "ab" 复制一份到字符串池,返回池地址给 s2
  • s1 在堆,s2 在池,所以 s1 == s2 是 false

JDK 7+:

  • s1.intern() 不再复制字符串到池,只是记录一下引用
  • 字符串池搬到了堆里,"ab" 本身就是从堆里产生的,直接记录引用就行
  • 所以 s1 和 s2 指向同一个对象,s1 == s2 是 true

三、intern 能省内存

适用场景:大量重复的字符串,只存一份能省不少内存。

java 复制代码
// 假设有 100 万个 userName,但实际取值只有几千种
String[] names = new String[1000000];
for (int i = 0; i < 1000000; i++) {
    names[i] = new String(getUserName()).intern();
}

intern 后,所有相同的字符串都指向字符串池里的同一个对象,内存占用会降下来。

但要注意:intern 会把字符串放入字符串池,如果取值种类太多(比如 UUID),反而浪费字符串池的内存。

四、常见用法:字符串比较

用 intern 做字符串字面量比较:

java 复制代码
// 不用 intern:每次比较都要遍历字符数组
if (str.equals("POST")) { }

// 用 intern:直接比较引用
if (str.intern() == "POST") { }

在高频比较场景下,引用比较比 equals 更快。

五、JDK 8 的陷阱

JDK 8 里字符串拼接和 intern 的交互比较绕:

java 复制代码
String a = "ab";
String b = "cd";
String c = "abcd";
String d = a + b;       // 编译期不会优化,结果是 new StringBuilder().append(a).append(b).toString()
String e = "ab" + "cd"; // 编译期直接优化成 "abcd"

System.out.println(c == d);  // false:c 是池,"abcd",d 是堆 new 出来的
System.out.println(c == e);  // true:e 在编译期就优化成 "abcd",也是池里的

所以 "ab" + "cd"a + b 看起来一样,但执行时机完全不同。

六、什么场景适合用 intern

场景 用 intern 原因
大量重复的字符串 节省内存
高频字符串比较 引用比较比 equals 快
取值种类极多的字符串 X 浪费字符串池空间
动态生成的字符串 X intern 本身也有开销

总结

  1. intern 返回字符串池里的引用
  2. JDK 6 会复制字符串到池,JDK 7+ 只是记录引用
  3. 用好了能省内存、做快速比较
  4. 用不好(取值种类太多)反而浪费内存

大多数业务代码里其实不太需要自己调用 intern,但如果面试问到,知道原理比背答案有用。

相关推荐
ZhengEnCi29 分钟前
J7A-高级Java工程师面试三道灵魂拷问-深度广度与工程素养的终极检验
java·后端
程序员七平4 小时前
面试官:你说你Vibe Coding手拿把掐,那 Claude Code 用户级、项目级、本地级配置怎么隔离?
面试
葫芦和十三5 小时前
图解 MongoDB 17|大集合与工作集:数据超过内存怎么办
后端·mongodb·面试
葫芦和十三13 小时前
图解 MongoDB 18|复制集拓扑:Primary、Secondary 和 Arbiter 的分工
后端·mongodb·面试
狼爷19 小时前
吃透 Java Function 接口,搞定 99% 的 Stream 场景
java·函数式编程
葫芦和十三19 小时前
图解 MongoDB 15|journal 与持久化:写入怎么不丢,崩溃怎么恢复
后端·mongodb·面试
葫芦和十三19 小时前
图解 MongoDB 16|压缩:snappy、zstd 和 zlib 的取舍
后端·mongodb·面试
祎雪双十Gy1 天前
从 DataX 的配置加载说起:我用 FastJson2 做了一个轻量级动态配置管理库
java·后端
labixiong1 天前
实现一个能跑的迷你版Promise(一)
前端·javascript·面试
小锋java12341 天前
分享一套锋哥原创的SpringBoot4+Vue3宠物领养网站系统
java