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

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

相关推荐
苦学编程啊1 小时前
【2025Flutter 入门指南】Dart SDK 安装与 VS Code 环境配置-Windows
android·dart
yuanManGan8 小时前
走进Linux的世界:初识操作系统(Operator System)
android·linux·运维
叶羽西8 小时前
Android15跟踪函数调用关系
android
消失的旧时光-19439 小时前
webView 的canGoBack/goBack 回退栈
android·webview
SHEN_ZIYUAN10 小时前
Flow 责任链模式图解
android
沐怡旸12 小时前
【底层机制】LeakCanary深度解析:从对象监控到内存泄漏分析的完整技术体系
android·面试
又菜又爱coding12 小时前
Android + Flutter打包出来的APK体积太大
android·flutter
LiuYaoheng12 小时前
【Android】Drawable 基础
android·java
Jerry14 小时前
构建 Compose 界面
android
Y多了个想法14 小时前
Linux驱动开发与Android驱动开发
android·linux·驱动开发