一、标准答案
可能创建1个或2个String对象,具体取决于字符串常量池中是否已存在"xxx":
-
如果常量池不存在"xxx":
-
在字符串常量池创建"xxx"对象(第1个)
-
在堆内存创建新的String对象(第2个)
-
总共创建2个对象
-
-
如果常量池已存在"xxx":
-
直接在堆内存创建新的String对象(第1个)
-
总共创建1个对象
-
二、详细解析
1. JVM内存分配过程
String s = new String("xxx");
执行步骤:
-
检查字符串常量池是否存在"xxx"
-
不存在:在常量池创建"xxx"字面量对象
-
已存在:直接引用现有字面量
-
-
在堆内存中新建String对象
-
将堆对象引用赋值给变量s
2. 内存结构示意图
情况1(常量池不存在"xxx"):
字符串常量池: "xxx" (0x100)
堆内存: String{value=0x100} (0x200)
变量s → 0x200
情况2(常量池已存在"xxx"):
字符串常量池: "xxx" (0x100) [已存在]
堆内存: String{value=0x100} (0x300)
变量s → 0x300
三、验证实验
1. 验证代码
public class StringCreation {
public static void main(String[] args) {
// 确保常量池没有"hello"
String s1 = new String("hello");
// 再次创建
String s2 = new String("hello");
System.out.println(s1.intern() == s2.intern()); // true
System.out.println(s1 == s2); // false
}
}
2. 使用javap查看字节码
// 第一次创建
0: new #2 // class java/lang/String
3: ldc #3 // String hello -> 从常量池加载
// 第二次创建
7: new #2 // class java/lang/String
10: ldc #3 // String hello -> 直接使用常量池已有
四、相关问题
Q1:String s = "xxx"会创建几个对象?
A:最多1个(仅在常量池不存在时创建)
Q2:如何证明字符串常量池的存在?
A:使用intern()
方法:
String s1 = new String("a") + new String("b");
s1.intern();
String s2 = "ab";
System.out.println(s1 == s2); // JDK6:false, JDK7+:true
Q3:new String("xxx").intern()的作用?
A:
-
确保字符串在常量池中存在
-
返回常量池中的引用
-
可以节省内存(重复字符串共享)
Q4:不同JDK版本对字符串常量池的处理差异?
A:
-
JDK6及之前:常量池在永久代(PermGen)
-
JDK7+:常量池移到堆内存
-
JDK8+:字符串去重等优化
五、最佳实践建议
-
避免不必要的new String():
// 不推荐 String s = new String("literal"); // 推荐 String s = "literal";
-
需要复制字符数组时使用:
char[] chars = getCharsFromIO(); String s = new String(chars); // 安全复制
-
大量重复字符串处理:
// 使用intern()需谨慎(可能引起性能问题) String canonical = largeString.intern();
理解字符串创建机制有助于写出更高效、更安全的Java代码,尤其在处理大量字符串时能有效控制内存使用。