小熊奶糖(BearCandy)
小熊奶糖(BearCandy)
发布于 2025-07-31 / 2 阅读
0
0

Java 字符串的两种创建方式

在 Java 中,String str1 = "hello";String str2 = "hello"; 使用 == 比较时返回 true,这与 字符串常量池(String Pool)JVM 的内存管理机制 有关。下面详细解释:


1. Java 字符串的两种创建方式

(1) 直接赋值(字面量方式)

String str1 = "hello";  // 存储在字符串常量池
String str2 = "hello";  // 复用常量池的"hello"
  • 字符串常量池(String Pool) 是 JVM 在 堆内存(Heap) 中开辟的一块特殊存储区域,用于存储字符串字面量(直接赋值的字符串)。
  • 当使用 "hello" 这样的字面量创建字符串时,JVM 会(也就是说相同的只存储一个):
    1. 先在字符串常量池中查找是否已存在 "hello"
    2. 如果存在,则直接返回该字符串的引用(不会新建对象)。
    3. 如果不存在,则在常量池中创建该字符串,并返回引用。

因此:

  • str1str2 指向同一个字符串常量池对象
  • str1 == str2 比较的是 引用地址,由于是同一个对象,返回 true

(2) 使用 new 关键字创建

String str3 = new String("hello");  // 强制在堆中新建对象
String str4 = new String("hello");  // 再新建一个对象
  • new String("hello") 会:
    1. 先在字符串常量池中查找 "hello"(如果不存在则创建)。
    2. 然后在堆内存中新建一个 String 对象,并指向常量池中的 "hello"(但 str3str4 本身是不同的对象)。
  • 因此:
    • str3 == str4 返回 false(不同对象)。
    • str3.equals(str4) 返回 true(内容相同)。

2. 内存结构分析

示例代码

String str1 = "hello";          // 常量池
String str2 = "hello";          // 复用常量池的"hello"
String str3 = new String("hello");  // 堆中新对象,但指向常量池的"hello"
String str4 = new String("hello");  // 另一个新对象

内存模型

栈 (Stack)           堆 (Heap)                 字符串常量池 (String Pool)
-------           -------------               -------------------
str1  -------->    String对象1  ------------>   "hello"
str2  --------/       (引用常量池)
str3  -------->    String对象2  ------------>   "hello"
str4  -------->    String对象3  ------------>   "hello"
  • str1str2 直接指向 字符串常量池 的同一个 "hello",所以 == 返回 true
  • str3str4堆中的不同对象,所以 == 返回 false(即使它们的内容相同)。

3. 关键结论

比较方式 str1 == str2 str1 == str3 str3 == str4
直接赋值 ("hello") true(同一常量池对象) false false
new String() false false false(不同堆对象)

最佳实践

  1. 比较字符串内容
    • 使用 equals()str1.equals(str2)(推荐)。
    • 使用 equalsIgnoreCase() 忽略大小写。
  2. 比较引用是否相同
    • 使用 ==(仅在需要判断是否是同一对象时使用,如字符串常量池优化场景)。

4. 扩展:intern() 方法

如果希望 new String("hello") 也返回常量池的引用,可以使用 intern()

String str3 = new String("hello").intern();  // 强制放入常量池(如果已存在则返回引用)
String str4 = "hello";
System.out.println(str3 == str4);  // true(现在指向同一个对象)
  • intern() 方法会尝试将字符串放入常量池(如果已存在则直接返回引用)。

总结

  • == 比较的是引用地址,直接赋值的字符串会复用常量池,所以 str1 == str2 返回 true
  • new String() 会强制创建新对象,即使内容相同,== 也会返回 false
  • 比较字符串内容时,始终使用 equals(),避免 == 的误用。

这样设计是为了 节省内存(避免重复字符串占用空间),同时提供灵活性(允许 new String 创建独立对象)。


评论