杰瑞科技汇

java string 赋值

Java 中字符串的赋值主要有两种方式:字面量赋值new 关键字赋值,这两种方式在底层机制上有根本的区别,决定了它们在内存中的表现。

java string 赋值-图1
(图片来源网络,侵删)

字面量赋值

这是最常见、最简单的赋值方式。

String str1 = "Hello World";
String str2 = "Hello World";

工作原理:字符串常量池

当你使用双引号 创建一个字符串时,JVM(Java 虚拟机)会执行以下操作:

  1. 检查常量池:JVM 首先会去一个叫做 字符串常量池 的特殊内存区域中查找,是否已经存在一个内容相同的字符串对象。
  2. 创建或复用
    • 如果常量池中没有这个字符串,JVM 就会在常量池中创建一个新的 String 对象,并将引用返回给变量 str1
    • 如果常量池中已经存在这个字符串(str2 赋值时),JVM 就不会创建新的对象,而是直接将常量池中已存在的对象的引用返回给变量 str2

内存示意图

      栈内存                  堆内存
      +-------+               +---------------------+
      | str1  | -------------> | "Hello World" (SCP) |
      +-------+               +---------------------+
      | str2  | ---------------^
      +-------+
  • SCP (String Constant Pool):字符串常量池,它是堆内存中的一块特殊区域。
  • 可以看到,str1str2 指向的是同一个内存地址。

验证:使用 和 equals()

  • 比较的是两个变量的内存地址(引用)是否相同。
  • equals():比较的是两个字符串对象的是否相同。
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2);      // 输出: true (因为它们指向同一个对象)
System.out.println(str1.equals(str2)); // 输出: true (因为它们的内容相同)

使用 new 关键字赋值

这种方式显式地告诉 JVM:“我要创建一个新的 String 对象”。

String str3 = new String("Hello");
String str4 = new String("Hello");

工作原理:堆内存

当你使用 new 关键字时,JVM 的行为完全不同:

java string 赋值-图2
(图片来源网络,侵删)
  1. 忽略常量池检查:JVM 不会去检查字符串常量池,它会直接在堆内存 中分配一块新的内存空间来创建 String 对象。
  2. 创建新对象是否相同,每次使用 new 都会创建一个全新的、独立的对象。
  3. (可选的) 常量池驻留:值得注意的是,在 new String("Hello") 这个过程中,字符串字面量 "Hello" 仍然会被先放入字符串常量池中。new 操作会在堆内存中再创建一个副本,变量 str3 指向的是堆内存中的这个副本。

内存示意图

      栈内存                  堆内存
      +-------+               +---------------------+
      | str3  | -------------> | "Hello" (堆)        |
      +-------+               +---------------------+
      | str4  | -------------> | "Hello" (堆)        |
      +-------+               +---------------------+
                              | "Hello" (SCP)       |  <-- 注意这个
                              +---------------------+
  • str3str4 指向的是两个不同的内存地址,尽管它们的内容相同。

验证:使用 和 equals()

String str3 = new String("Hello");
String str4 = new String("Hello");
System.out.println(str3 == str4);      // 输出: false (因为它们指向不同的对象)
System.out.println(str3.equals(str4)); // 输出: true (因为它们的内容相同)

混合情况分析

当字面量和 new 混合使用时,情况会更有趣。

String str5 = "Hello";       // 字面量,从常量池获取
String str6 = new String("Hello"); // new,在堆内存创建新对象
System.out.println(str5 == str6);      // 输出: false
System.out.println(str5.equals(str6)); // 输出: true

内存示意图

      栈内存                  堆内存
      +-------+               +---------------------+
      | str5  | -------------> | "Hello" (SCP)       |
      +-------+               +---------------------+
      | str6  | -------------> | "Hello" (堆)        |
                              +---------------------+
  • str5 指向常量池中的 "Hello"
  • str6 指向堆内存中由 new 创建的 "Hello"
  • 它们是两个完全不同的对象,str5 == str6false

intern() 方法

intern() 是一个特殊的方法,它可以将一个字符串“驻留”到字符串常量池中。

String str7 = new String("Hello"); // str7 在堆中
String str8 = str7.intern();      // str8 指向常量池中的 "Hello"
// str7.intern() 会返回常量池中 "Hello" 的引用
// 因为 "Hello" 字面量已经存在于常量池了
System.out.println(str7 == str8);      // 输出: false
System.out.println(str5 == str8);      // 输出: true (str5是字面量,也指向常量池)

intern() 的工作流程:

java string 赋值-图3
(图片来源网络,侵删)
  1. 检查字符串常量池中是否已经存在与调用对象内容相同的字符串。
  2. 如果存在,直接返回常量池中该字符串的引用。
  3. 如果不存在,则将堆中的这个字符串对象复制一份到字符串常量池中,然后返回常量池中这个新对象的引用。

总结与最佳实践

特性 字面量赋值 (String s = "abc";) new 关键字赋值 (String s = new String("abc");)
创建位置 字符串常量池 堆内存
内存效率 的字符串只存一份,节省内存。 ,每次都创建新对象,可能造成内存浪费。
执行速度 稍快,直接从常量池引用,无需在堆中分配新对象。 稍慢,涉及在堆中分配新对象的过程。
比较 的字符串字面量, 结果为 true 相同, 结果也为 false
推荐场景 绝大多数情况下都推荐使用字面量,这是 Java 语言的设计初衷,也是最符合直觉的方式。 需要创建一个全新的、独立的字符串对象时(为了在多线程环境中安全地修改,但String本身不可变,所以此场景较少)。
从 IO 流(如文件、网络)中读取数据并构建字符串时。

核心建议:

在 Java 中,除非有特殊需求,否则请始终使用字面量来创建和赋值字符串。 这不仅能提高代码的可读性,还能带来更好的性能和内存效率。

String 的一个重要特性:不可变性,一旦一个 String 对象被创建,它的内容就不能被改变,任何看起来像“修改”字符串的操作(如 concat(), replace(), substring() 等),实际上都是创建了一个新的 String 对象来保存修改后的结果,而原始对象保持不变。

分享:
扫描分享到社交APP
上一篇
下一篇