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

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

相关推荐
Jerry12 分钟前
问题记录 - Android IdleHandler 没有执行
android
没有了遇见27 分钟前
Android ButterKnife Android 35情况下 适配 Gradle 8.+
android
方白羽1 小时前
Android多层嵌套RecyclerView滚动
android·java·kotlin
菜就多学2 小时前
SurfaceControlViewHost 实现跨进程UI渲染
android·设计
2501_915106322 小时前
iOS App 测试工具全景分析,构建从开发调试到线上监控的多阶段工具链体系
android·测试工具·ios·小程序·uni-app·iphone·webview
小羊在奋斗3 小时前
MySQL表的约束:从基础到核心(附场景+案例)
android·数据库·mysql
e***19354 小时前
MySQL-mysql zip安装包配置教程
android·mysql·adb
方白羽4 小时前
Kotlin遇上Java 静态方法
android·java·kotlin
q***06475 小时前
SpringSecurity相关jar包的介绍
android·前端·后端
7***31886 小时前
若依微服务中配置 MySQL + DM 多数据源
android·mysql·微服务