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个对象)

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

相关推荐
用户2018792831678 小时前
用 “建房子” 讲懂 Android 中 new 对象的全过程:从代码到 ART 的魔法
android
手机不死我是天子8 小时前
《Android 核心组件深度系列 · 第 4 篇 ContentProvider》
android·架构
鹏多多8 小时前
flutter-切换状态显示不同组件10种实现方案全解析
android·前端·ios
FengyunSky9 小时前
高通Camx内存问题排查
android·linux·后端
00后程序员张10 小时前
苹果软件混淆的工程逻辑,从符号空间到资源扰动的体系化实现
android·ios·小程序·https·uni-app·iphone·webview
alexhilton18 小时前
突破速度障碍:非阻塞启动画面如何将Android 应用启动时间缩短90%
android·kotlin·android jetpack
kobe_OKOK_18 小时前
Django `models.Field` 所有常见配置参数的完整清单与说明表
android
前行的小黑炭20 小时前
Android Compose :初步了解一下生命周期,对比原生android
android·kotlin·app
湖南人爱科技有限公司21 小时前
RaPhp和Python某音最新bd-ticket-guard-client-data加密算法解析(视频评论)
android·python·php·音视频·爬山算法·raphp