java中string对象创建关键看jvm行为:字面量走常量池复用,new在堆新建对象,intern手动将堆对象登记入池;故==比较结果取决于是否同源池引用。

Java 中 String 对象怎么创建,不只看代码写了啥,关键要看 JVM 背后做了什么:字面量走常量池(复用)、new 走堆(新建)、intern 是手动“拉进池”的开关。理解这三点,就能解释为什么 == 有时真、有时假,也能避开无谓的对象膨胀。
字面量赋值:先查池、再复用
写 String s = "hello"; 时,JVM 在类加载阶段就把 "hello" 放入字符串常量池(JDK7 起池在堆内存中,但逻辑独立)。运行时执行这行代码,会:
- 检查常量池中是否已存在内容完全相同的字符串(按 Unicode 序列逐字符比)
- 存在 → 直接返回池中对象的引用,不新建
- 不存在 → 在池中新建一个,并返回其引用
所以 String a = "abc"; String b = "abc"; 中,a == b 为 true,因为它们指向同一个池内对象。
new 创建:堆里必新、池里可有
调用 new String("hello") 时,JVM 一定在堆中新建一个 String 实例,和常量池无关。但构造参数 "hello" 本身是字面量,会触发常量池检查:
- 若池中尚无
"hello",先在池中创建一份 - 再在堆中新建 String 对象,其内部
value字段(JDK9+ 是byte[])指向池中的字符数据
因此 new String("abc") 至少创建 1 个堆对象;常量池中最多新增 1 个对象(仅当之前没有)。String x = new String("abc"); String y = "abc"; 中,x == y 恒为 false,因地址不同。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
intern():把堆对象“登记”进池
intern() 是唯一能主动把堆中字符串“拉进”常量池的方法:
- 调用
s.intern()时,JVM 查常量池是否有内容相等的字符串 - 有 → 返回池中已有引用
- 没有 → 把该字符串内容加入池,再返回新加入对象的引用
例如 new String("test").intern() == "test" 总是 true。注意:JDK7+ 后池在堆中,intern() 返回的是堆内池对象的引用,不是“复制到别处”。
拼接行为:编译期优化 vs 运行时新建
字符串拼接结果是否进池,取决于是否能在编译期确定全部内容:
-
"a" + "b"→ 编译器直接优化为"ab",走字面量流程,进池 -
"a" + s(s是变量)→ 运行时通过StringBuilder构建,结果默认在堆中,不自动入池 - 频繁拼接建议显式用
StringBuilder,避免每次生成临时 String 对象
不可变性贯穿始终:所有修改方法(如 concat、substring)都返回新对象,原对象不变,这是复用安全与哈希缓存的前提。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










