Java经典一问:String s = new String("xxx");创建了几个String对象?

用一个超有趣的故事给你讲明白这个"世纪难题"。

先上结论(让你心里有数)

String s = new String("xxx"); 可能创建1个或2个String对象 ,关键看"xxx"在字符串常量池里是否存在。

故事时间:图书馆与复印机

想象一下有个超级图书馆(字符串常量池):

场景1:第一次借阅(创建2个对象)

java 复制代码
String s = new String("Android大师");

故事剧情:

  1. 📚 图书馆采购新书 :图书管理员发现图书馆没有《Android大师》这本书,于是新买一本 放入图书馆书架上("Android大师"字面量在常量池创建)
  2. 📋 制作借书卡 :你在图书馆目录里登记这本书的信息(引用指向常量池
  3. 🖨️ 复印一本书 :你用复印机重新复印一本 《Android大师》(new在堆内存创建新对象)
  4. 🎒 带回家 :你把复印本带回家,变量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大师");

故事剧情:

  1. 📚 图书馆已有藏书:图书管理员发现《Android大师》已经在书架上
  2. 🖨️ 直接复印 :你直接用复印机复印一本(只需在堆内存创建新对象)
  3. 🎒 带回家:同样把复印本带回家

深度原理剖析

字符串常量池(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个对象)

现在你明白为什么这个问题这么经典了吧?理解这个对内存优化和性能调优很有帮助!

相关推荐
工程师老罗5 小时前
如何在Android工程中配置NDK版本
android
Libraeking8 小时前
破壁行动:在旧项目中丝滑嵌入 Compose(混合开发实战)
android·经验分享·android jetpack
市场部需要一个软件开发岗位9 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
JMchen12310 小时前
Android后台服务与网络保活:WorkManager的实战应用
android·java·网络·kotlin·php·android-studio
crmscs11 小时前
剪映永久解锁版/电脑版永久会员VIP/安卓SVIP手机永久版下载
android·智能手机·电脑
localbob11 小时前
杀戮尖塔 v6 MOD整合版(Slay the Spire)安卓+PC端免安装中文版分享 卡牌肉鸽神作!杀戮尖塔中文版,电脑和手机都能玩!杀戮尖塔.exe 杀戮尖塔.apk
android·杀戮尖塔apk·杀戮尖塔exe·游戏分享
机建狂魔11 小时前
手机秒变电影机:Blackmagic Camera + LUT滤镜包的专业级视频解决方案
android·拍照·摄影·lut滤镜·拍摄·摄像·录像
hudawei99611 小时前
flutter和Android动画的对比
android·flutter·动画
lxysbly13 小时前
md模拟器安卓版带金手指2026
android
儿歌八万首14 小时前
硬核春节:用 Compose 打造“赛博鞭炮”
android·kotlin·compose·春节