⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 网络 「网络」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

面试考察点

这道”经典八股”背后,面试官真正想听的是:

  1. JVM 内存模型是否清晰:字符串常量池和堆,这两块地盘你分得清吗?
  2. new String(...) 和字面量赋值的本质区别:不是背结论,是说清楚底层发生了什么。
  3. 字符串常量池的驻留机制:intern() 干了啥,常量池什么时候创建对象、什么时候复用。
  4. 思维严谨度:能不能分场景讨论,而不是张口就来”两个”。

核心答案

1 个或 2 个,取决于常量池里有没有 "abc"。

  • 常见情况:2 个对象。

    • 对象 1(字符串常量池):字面量 "abc" 首次出现,JVM 在常量池中创建它。
    • 对象 2(堆):new String(...) 在堆上再创建一个副本。
  • 特殊情况:1 个对象。

    • 如果常量池里已经有 "abc"(之前的代码用过这个字面量,或者调用过 intern()),那字面量不会重复创建,只有堆上那个 new 出来的对象。

深度解析

原理/机制

字符串常量池是 JVM 为了省内存搞的”字符串缓存池”。JDK 7 之前放在方法区,JDK 7 之后挪到了堆里。核心逻辑很简单:

  • 双引号字面量(如 "abc")出现时,JVM 先查常量池。有就直接返回引用,没有就创建一个再返回。
  • 所有相同内容的字面量共享同一个对象——这就是为什么 "abc" == "abc" 是 true。

new String(String original) 构造函数则完全不同:它 无条件在堆上创建新对象,把参数的字符数组拷过去。不管常量池里有没有,new 就是 new,堆上必出新对象。

代码示例与内存分析

public class StringCreationDemo {
public static void main(String[] args) {
// ===== 场景一:常量池无 "abc",创建 2 个对象 =====
String str1 = new String("abc");
// 1. 字面量 "abc" 首次出现 → 常量池创建对象 O1
// 2. new 关键字 → 堆上创建对象 O2(复制 O1 的字符数组)
// str1 指向堆上的 O2

// ===== 场景二:常量池已有 "abc",只创建 1 个对象 =====
String str2 = new String("abc");
// 字面量 "abc" 已存在于常量池,直接复用 O1
// new 只在堆上创建 O3
// str2 指向堆上的 O3

// ===== 验证:用 == 和 equals 看清引用关系 =====
String literal = "abc"; // 直接指向常量池中的 O1

System.out.println(str1 == str2); // false — 两个 new 出来的堆对象,地址不同
System.out.println(str1 == literal); // false — 堆对象 vs 常量池对象,地址不同
System.out.println(str1.equals(str2));// true — 内容相同

// ===== intern():手动把字符串"塞进"常量池 =====
String interned = str1.intern(); // 返回常量池中 "abc" 的引用(O1)
System.out.println(interned == literal); // true — intern() 返回的就是常量池那个对象
System.out.println(str1 == interned); // false — str1 还是指向堆上的 O2

// ===== 结论:业务代码永远用字面量,别 new String =====
String right = "abc"; // ✅ 复用常量池,省内存
String wrong = new String("abc"); // ❌ 白白多创建一个堆对象,没意义
}
}

对比分析与最佳实践

方式 对象数 == 行为 性能
String str = "abc" 0 或 1 个(常量池复用) 相同字面量 == 为 true 省内存
String str = new String("abc") 必然 1 个堆对象 str == "abc" 为 false 多一次堆分配,没必要

结论:业务代码中永远用字面量方式。 new String(String) 在实际开发中几乎没有正当使用场景。你能想到的唯一理由——“需要一个全新的、和任何引用都不 == 的对象”——这种需求约等于不存在。

常见误区

  1. 把引用当对象:str 是引用,不是对象。问的是”创建了几个对象”,别把引用也算进去。
  2. 张口就说”2 个”:没考虑常量池可能已经有 "abc" 的情况。面试官追问”一定是 2 个吗?”你就懵了。
  3. intern() 的误解:new String("abc").intern() 返回的是常量池中的引用,堆上那个 new 出来的对象没人引用,等着被 GC 回收。intern() 不创建新对象,它只是”查池返引用”。
文章目录
  1. 1. 面试考察点
  2. 2. 核心答案
  3. 3. 深度解析
    1. 3.1. 原理/机制
    2. 3.2. 代码示例与内存分析
    3. 3.3. 对比分析与最佳实践
    4. 3.4. 常见误区