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,但如果面试问到,知道原理比背答案有用。

相关推荐
清水白石0085 小时前
Python 编程实战全景:从基础语法到插件架构、异步性能与工程最佳实践
开发语言·python·架构
yaoxin5211235 小时前
390. Java IO API - WatchDir 示例
java·前端·python
Halo_tjn7 小时前
Java 基于字符串相关知识点
java·开发语言·算法
梦想的颜色7 小时前
java 利用redis来限制用户频繁点击
java·开发语言
报错小能手7 小时前
Swift 并发 Combine响应式框架
开发语言·ios·swift
万法若空8 小时前
C++ <memory> 库全方位详解
开发语言·c++
代码中介商8 小时前
C++ 类型转换深度解析:static_cast、dynamic_cast、const_cast、reinterpret_cast
开发语言·c++
青小莫8 小时前
C++之string(OJ练习)
开发语言·c++·stl
freshman_y8 小时前
一篇介绍C语言中二级指针和二维数组的文章
c语言·开发语言
-Marks-8 小时前
【C++编程】STL简介 --- (是什么 | 版本发展历程 | 六大组件 | 重要性缺陷以及如何学习)
开发语言·c++·学习·stl·stl版本