Sunday面试指南

Java 字符串常量池是什么?String.intern()、字面量和 new String 有什么区别?

下面是一段教学用的模拟面试。

🧑‍💻 面试官:两个内容相同的字符串,用 == 比较一定是 true 吗?

🙋‍♂️ 我:不一定。== 比较引用,equals 比较字符串内容。

🧑‍💻 面试官:那两个 “CN” 字面量,为什么又能用 == 得到 true?

🙋‍♂️ 我:因为字符串常量池会复用它们。

🧑‍💻 面试官:如果其中一个来自 new String(“CN”),再调用 intern(),到底哪一个对象被复用了?

这道题要分开看两件事:「内容相同」和「引用相同」。常量池解决复用,业务比较仍然应该看内容。

面试速答(60 秒版)

Java 的字符串池会为相同内容保存一个规范的 String 对象。字符串字面量,以及值为字符串的常量表达式,会使用池里的对象。

而 new String("CN") 会创建一个新的 String 对象,所以它和字面量内容相同,引用却不一定相同。调用 intern() 后,如果池里已经有相同内容的字符串,就返回那个对象;如果没有,就把当前对象加入池并返回它。

因此,intern 并不是把原变量指向的对象直接改掉。只有接住它的返回值,才能拿到规范引用。

实际开发中,我们还是用 equals 比较内容。不要为了让 == 可用,就给所有字符串调用 intern。是否驻留,需要看重复程度、字符串数量和实际内存收益。

相同内容,不一定同一对象

图:相同内容,不一定同一对象。

知识点详解:字符串相同,到底是哪一层相同?

先把变量、对象和字符串池分开

假设两个模块都要保存区域编码 CN。模块 A 直接使用字面量,模块 B 则从接口取得文本。业务上,我们关心的是两个区域编码是否相同;虚拟机还需要处理另一件事:这两段文本要不要共享同一个 String 对象。

变量里保存的是对象的引用。两个变量可以指向同一个对象,也可以各自指向一个内容相同的对象。== 判断前一种关系,equals 判断后一种关系。

字符串池可以理解为一份按内容寻找规范对象的登记表。但这只是帮助理解的比喻,不意味着每次创建 String 都要自动去这里登记。

字面量、new 和 intern 分别做了什么?

把几种常见写法放在一起看,区别就清楚了:

写法得到的引用判断依据
a = "CN"、b = "CN"相同的池内引用字面量驻留
c = new String("CN")新对象的引用new 创建对象
d = c.intern()与 a 相同的规范引用池里已存在 CN

因此,a 与 c 用 == 比较是 false,用 equals 比较是 true;a 与 d 用 == 比较则是 true。c 本身不会因为调用了 intern 就自动改为池内引用。

这里也不能把 new 的例子简单背成“这一行一定创建两个对象”。字面量对应的对象可能早已存在。要问新增了几个对象,需要交代执行前的池状态,以及统计的是哪一段执行过程。

以上驻留和返回规则来自 String API;字面量的规范由 Java 语言规范 定义。

intern 返回引用,不改原变量

图:intern 返回引用,不改原变量。

字符串拼接,也要看能否在编译期确定

"C" + "N" 是常量表达式,它的结果可以在编译期确定,使用驻留后的 CN。

但是,如果 p 是运行时取得的普通变量,p + "N" 就不能因为当前 p 恰好等于 C,便认定结果一定是池内对象。运行时拼接产生的字符串,不能照搬字面量的引用结论。

final 也不是一句万能解释。final String p = "C" 可以是常量变量;但 final String p = readPrefix() 的值需要运行时获得,并不是相同的情况。

本题讨论的是 Java 的对象身份和语言规则,没有同一套 TypeScript 或 Python API。其他语言即使存在驻留优化,也不能拿来证明 Java 的行为。

为什么不把所有字符串都 intern?

回到区域编码的场景。如果只有少量固定编码,池内复用可能很合适;如果是随机请求 ID、用户输入和不断变化的长文本,驻留并不一定有明显收益,还会增加查找和管理成本。

应该先确认重复字符串是否真的占了大量内存,再用实际堆分析和负载测试判断收益。也不要把旧版本的“字符串池永远在某个固定内存区”作为通用答案,内存实现需要说明 JVM 和版本。

面试官继续追问

那我统一用 intern,再用 == 比较,行不行?

在保证双方都取得规范引用时,== 可以判断引用一致。但这会让业务正确性依赖每一条输入路径都做驻留。漏掉一个分支,就可能出现内容相同却判断不同的问题。对于普通字符串业务,equals 更直接。

intern 会修改 String 的内容吗?

不会。String 内容不可变,intern 返回的是规范对象引用,不是修改字符串内容的操作。

null 怎么比较?

如果调用方可能为 null,就不能直接调用它的 equals。可以明确判空,或者使用允许 null 的内容比较工具。这个问题与常量池没有关系。

面试速记卡

  • ==:比较引用是否相同;equals:比较字符串内容。
  • 字面量和字符串常量表达式:使用驻留后的对象。
  • new String:创建新对象,内容相同不代表引用相同。
  • intern:返回规范引用,不会替你修改原变量。
  • 工程选择:先证明复用有收益,不把驻留当成业务比较的前提。

公司面试真题

这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。

浏览公司面试真题 →
简历汪永久免费在线制作简历,模板直接套用、导出无水印,永久免费、下载免费,不需要付费解锁任何功能。去写简历