双引号赋值优先复用字符串常量池中实例,new构造总在堆中新建对象;常量池存编译期字面量、自动去重,堆中new的对象地址唯一;intern可强制入池;栈中仅存引用,不存对象实体。

Java中创建String对象时,用双引号直接赋值和用new关键字构造会产生本质不同的内存行为——前者优先复用字符串常量池中的已有实例,后者总是在堆中新建对象。
字符串常量池:编译期确定、自动复用
当你写 String s = "hello";,JVM在类加载阶段就会把字面量"hello"放入字符串常量池(JDK7起位于堆中,逻辑上仍称“常量池”)。后续再出现相同字面量,如 String t = "hello";,s 和 t 指向的是同一个对象地址。
- 常量池中只保留一份相同内容的字符串,节省内存
- 比较引用是否相等(==)时结果为true
- 所有编译期已知的字符串字面量都进入常量池
堆内存:运行期创建、独立实例
使用 new String("hello") 时,JVM一定在堆中分配新对象。即使常量池里已有"hello",也会额外创建一个堆内String实例,其内部char数组(value字段)可能引用常量池中的字符数据,但String对象本身是独立的。
- 每次new都会产生新地址,==比较结果为false
- 堆中对象由GC管理,生命周期更灵活
- new操作实际做了两件事:确保常量池有该字符串;再在堆中构建新String对象
拼接与intern:绕过默认规则的关键操作
变量拼接(如 s1 + s2)默认生成堆中字符串,不会自动进常量池。但可调用 .intern() 强制将其加入或关联到常量池:
-
new String("abc").intern() == "abc"返回true(因为intern返回常量池中引用) - 拼接结果调用intern后,若池中无此串则加入,有则返回已有引用
- 注意:intern在JDK7+后效率提升,因常量池移到堆中,避免跨区域复制
栈中存什么:只是引用,不是对象
无论String对象在常量池还是堆中,变量本身(如String s)的引用值都存在栈帧的局部变量表里。栈不存String内容,只存指向真实对象的地址。
- 栈中引用可以指向常量池里的字符串,也可以指向堆里的String实例
- 方法结束时,栈中引用消失,但堆/常量池中的对象是否回收取决于是否有其他引用
- 栈空间小、快、自动释放;对象实体的存储位置才决定内存行为差异
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











