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

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

相关推荐
Kapaseker1 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴2 小时前
Android17 为什么重写 MessageQueue
android
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android