String str = new String("abc") 创建了几个对象?
2026年01月16日
一则或许对你有用的小广告
欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于
Spring AI + Spring Boot3.x + JDK 21..., 点击查看; - 《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot3.x + JDK 17..., 点击查看项目介绍; 演示链接: http://116.62.199.48:7070/; - 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/
面试考察点
面试官提出这个问题,通常旨在考察候选人以下几个层面的理解:
- 对 JVM 内存模型(特别是方法区/元空间和堆)的掌握:能否清晰区分字符串常量池与堆内存。
- 对
String类两种创建方式本质的理解:明确new String(...)与字面量赋值的根本区别。 - 对字符串常量池(String Pool)机制的理解:它如何工作,以及
intern()方法的作用。 - 严谨的思考逻辑:能否清晰地分析代码执行时,对象是在何时、于何处创建的。
核心答案
这个问题的答案是:1 个或 2 个。
具体来说:
- 通常情况下,会创建 2 个对象。
- 对象 1 (位于堆中):
new String(...)会在 Java 堆内存中创建一个新的String实例对象。 - 对象 2 (位于字符串常量池中):字面量
"abc"会促使 JVM 在字符串常量池(String Pool)中查找或创建对应的字符串对象。
- 对象 1 (位于堆中):
- 特殊情况下,只会创建 1 个对象。
- 如果在 此代码执行之前,字符串常量池中已经存在内容为
"abc"的对象(例如,其他代码已经使用过"abc"字面量或调用了"abc".intern()),那么String str = new String("abc")中的字面量"abc"将不会触发新的创建,而只是指向池中已存在的对象。此时,仅会在堆中创建 1 个新的String对象。
- 如果在 此代码执行之前,字符串常量池中已经存在内容为
深度解析
原理/机制
- 字符串常量池:是 JVM 为了提升性能和减少内存开销而设计的一块特殊内存区域(在 JDK 7 之前位于方法区,JDK 7 及之后移至堆中)。它的核心机制是 “驻留(intern)”。当使用双引号字面量(如
"abc")创建字符串时,JVM 会首先检查常量池中是否存在内容相等的字符串对象。- 如果存在,则直接返回池中对象的引用。
- 如果不存在,则在池中创建该对象,并返回其引用。
new String(String original)构造函数:这个构造函数的官方文档说明是:Initializes a newly created String object so that it represents the same sequence of characters as the argument; in other words, the newly created string is a copy of the argument string.它的作用是 在堆上创建一个新的String对象,并将参数字符串的字符数组拷贝(或引用,具体实现有优化)过去。因此,new操作必然在堆中产生一个新对象。
代码示例与内存分析
// 场景一:创建2个对象
public class StringCreationDemo {
public static void main(String[] args) {
// 假设这是程序中第一次出现 "abc"
String str = new String("abc");
// 步骤分解:
// 1. 字面量 `"abc"` 出现,JVM 检查常量池,无 "abc",于是在常量池创建对象O1。
// 2. `new` 关键字在堆上申请内存,创建新的 String 对象 O2。
// 3. 构造函数将 O1 的内容(字符数组)初始化给 O2。
// 4. 引用 `str` 指向堆中的对象 O2。
}
}
// 场景二:创建1个对象
public class StringCreationDemo2 {
public static void main(String[] args) {
String constant = "abc"; // 此行代码执行后,常量池中已有 "abc" 对象 O_pool
// ... 其他代码
String str = new String("abc"); // 此时,字面量 `"abc"` 直接引用已存在的 O_pool,仅在堆上创建1个新对象 O_heap。
}
}
对比分析与最佳实践
String str = "abc";vsString str = new String("abc");- 字面量方式:可能创建 0 或 1 个对象(仅在常量池不存在时创建),且所有字面量相同的引用都指向常量池中同一个对象,节省内存,性能更优。
new方式:必然在堆上创建新对象(1个),且与常量池中的对象是 两个不同的对象(但equals()为 true)。str == "abc"结果为false。
- 最佳实践:在绝大多数业务开发场景中,强烈推荐使用字面量方式(
String s = "abc")来创建字符串。除非有明确的需求(例如,需要保证得到一个全新的、与其他任何引用都不==的对象,但这种场景极少),否则应避免使用new String(String)构造函数,因为它会产生不必要的对象开销。
常见误区
- 混淆引用和对象:变量
str只是一个引用,它本身不是对象。问题问的是 “创建了几个对象”。 - 忽略常量池的已存在情况:武断地回答 “总是 2 个”,没有考虑常量池的缓存机制。
- 混淆
intern()方法的作用:new String("abc").intern()这个表达式整体,在常量池已存在时不会创建新对象,它会返回池中对象的引用。此时,堆中新创建的对象在没有其他引用的情况下,稍后会被 GC 回收。
总结
String str = new String("abc") 在大多数首次出现该字面量的情况下会创建 2 个对象(1个在常量池,1个在堆),但其根本机制依赖于 JVM 的字符串常量池,在池中已存在对应对象时则只创建 1 个堆对象。