用一个超有趣的故事给你讲明白这个"世纪难题"。
先上结论(让你心里有数)
String s = new String("xxx");
可能创建1个或2个String对象 ,关键看"xxx"
在字符串常量池里是否存在。
故事时间:图书馆与复印机
想象一下有个超级图书馆(字符串常量池):
场景1:第一次借阅(创建2个对象)
java
String s = new String("Android大师");
故事剧情:
- 📚 图书馆采购新书 :图书管理员发现图书馆没有《Android大师》这本书,于是新买一本 放入图书馆书架上(
"Android大师"
字面量在常量池创建) - 📋 制作借书卡 :你在图书馆目录里登记这本书的信息(引用指向常量池)
- 🖨️ 复印一本书 :你用复印机重新复印一本 《Android大师》(
new
在堆内存创建新对象) - 🎒 带回家 :你把复印本带回家,变量
s
就是你家那本复印书
代码验证:
java
String s1 = new String("Android大师");
String s2 = new String("Android大师");
String s3 = "Android大师";
System.out.println(s1 == s2); // false - 两本不同的复印书
System.out.println(s1 == s3); // false - 复印书 vs 图书馆原书
System.out.println(s2 == s3); // false - 同上
场景2:再次借阅(创建1个对象)
java
// 假设之前已经有人借过这本书
String existing = "Android大师"; // 图书馆已有这本书
// ... 之后某处 ...
String s = new String("Android大师");
故事剧情:
- 📚 图书馆已有藏书:图书管理员发现《Android大师》已经在书架上
- 🖨️ 直接复印 :你直接用复印机复印一本(只需在堆内存创建新对象)
- 🎒 带回家:同样把复印本带回家
深度原理剖析
字符串常量池(String Constant Pool)
java
// 字符串常量池是JVM的特殊内存区域,类似于缓存
// 字面量创建方式会检查并复用常量池中的字符串
String a = "hello"; // 第一次,创建并放入常量池
String b = "hello"; // 第二次,直接复用常量池中的
System.out.println(a == b); // true - 同一个对象!
new String() 的工作原理
java
public class StringDemo {
public static void main(String[] args) {
// 分解步骤看执行过程
String literal = "Android大师"; // 步骤1:检查常量池,不存在则创建
String newStr = new String(literal); // 步骤2:在堆中创建新对象
// 内存地址比较
System.out.println("字面量地址: " + System.identityHashCode(literal));
System.out.println("new对象地址: " + System.identityHashCode(newStr));
}
}
时序图:完整的创建过程

实际代码演示
java
public class StringCreationDemo {
public static void main(String[] args) {
System.out.println("=== 第一次创建 ===");
String firstNew = new String("Android大师");
demonstrateCreation(firstNew, "firstNew");
System.out.println("\n=== 第二次创建 ===");
String secondNew = new String("Android大师");
demonstrateCreation(secondNew, "secondNew");
System.out.println("\n=== 字面量创建 ===");
String literal = "Android大师";
demonstrateCreation(literal, "literal");
}
static void demonstrateCreation(String str, String varName) {
System.out.println(varName + " 内存地址: " + System.identityHashCode(str));
System.out.println(varName + " 值: " + str);
// 检查是否在常量池中
String interned = str.intern();
System.out.println(varName + " intern() 地址: " + System.identityHashCode(interned));
}
}
Android开发中的实际建议
作为Android老司机,我给你些实用建议:
java
// ❌ 避免这样写 - 浪费内存
String s1 = new String("hello");
// ✅ 推荐这样写 - 利用常量池
String s2 = "hello";
// ✅ 特殊情况:需要从char数组创建新对象时
char[] data = {'h', 'e', 'l', 'l', 'o'};
String s3 = new String(data); // 这是合理的
总结
记住这个简单的比喻:
- 字符串常量池 = 图书馆原版书
- new String("xxx") = 复印图书馆的书
- 第一次复印:需要先买书再复印(创建2个对象)
- 后续复印:直接拿现有书复印(创建1个对象)
现在你明白为什么这个问题这么经典了吧?理解这个对内存优化和性能调优很有帮助!