Java 中的 String 赋值操作本质上是 引用的复制,而不是内容的复制。

下面我们来详细分解这个过程。
核心概念:String 不可变性与引用
要理解两个关键点:
String的不可变性:在 Java 中,String对象一旦被创建,其内容(值)就不能被改变,任何对字符串的修改操作(如concat(),replace()等)实际上都是创建了一个新的String对象,而不是修改原来的对象。- 引用类型:
String是一个引用类型,当你声明一个String变量时,你实际上是在创建一个引用(可以理解为指针或地址),它指向内存中存储字符串数据的地方。
String str1; // 只声明了一个引用变量 str1,它目前不指向任何对象 str1 = "Hello"; // 创建了一个 "Hello" 对象,str1 引用指向它
赋值操作:str2 = str1; 的过程
让我们通过一个具体的例子来理解赋值过程。
String str1 = "Hello"; String str2 = str1; // 关键步骤:将 str1 赋值给 str2
这个过程发生了什么?

-
创建
str1:- JVM 在字符串常量池中查找是否存在 "Hello" 这个字符串。
- 如果不存在,就在池中创建一个新的 "Hello" 对象。
- 如果已经存在,就直接复用这个对象。
- 变量
str1被创建,它的值是池中 "Hello" 对象的内存地址。
-
赋值
str2 = str1:- 这行代码并没有在字符串常量池中创建新的 "Hello" 对象。
- 它执行的是引用复制:将变量
str1中存储的内存地址复制一份,然后赋给变量str2。 str1和str2这两个变量都指向了内存中同一个 "Hello" 对象。
内存示意图:
+-------------------+
| 字符串常量池 |
+-------------------+
| |
| "Hello" 对象 | <--- 内存地址 0x1234
| |
+-------------------+
^
|
+----------------+ +----------------+
| str1 变量 | | str2 变量 |
| (引用/地址) |----->| (引用/地址) |
| 0x1234 | | 0x1234 |
+----------------+ +----------------+
代码示例与验证
让我们通过代码来验证这个行为。

public class StringAssignment {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = str1; // str2 引用指向 str1 指向的对象
System.out.println("初始状态:");
System.out.println("str1: " + str1); // 输出: Hello
System.out.println("str2: " + str2); // 输出: Hello
System.out.println("str1 == str2 ? " + (str1 == str2)); // 输出: true
// (== 比较的是引用地址,因为它们指向同一个对象,所以为 true)
System.out.println("\n修改 str2...");
// 因为 String 不可变,str2.concat(" World") 实际上创建了一个新的 "Hello World" 对象
// str2 本身并没有改变,它仍然指向原来的 "Hello" 对象
str2 = str2.concat(" World");
System.out.println("修改后状态:");
System.out.println("str1: " + str1); // 输出: Hello (str1 指向的对象未受影响)
System.out.println("str2: " + str2); // 输出: Hello World (str2 现在指向了新的对象)
System.out.println("str1 == str2 ? " + (str1 == str2)); // 输出: false
// (str1 和 str2 指向了不同的对象,所以为 false)
}
}
输出结果:
初始状态:
str1: Hello
str2: Hello
str1 == str2 ? true
修改 str2...
修改后状态:
str1: Hello
str2: Hello World
str1 == str2 ? false
这个例子清晰地证明了:
- 赋值是引用的传递,
str1和str2一开始指向同一个对象。 - 对
str2的“修改”操作,由于String的不可变性,实际上是让str2指向了一个新创建的对象,而str1仍然指向原来的对象。
特殊情况:new String() 构造函数
使用 new 关键字创建 String 对象时,情况会有所不同。new 强制在堆内存中创建一个全新的对象,而不会去检查字符串常量池。
String s1 = new String("Hello");
String s2 = new String("Hello");
在这个例子中:
s1指向堆内存中的一个 "Hello" 对象。s2指向堆内存中另一个全新的 "Hello" 对象。- 虽然
s1.equals(s2)是true(因为equals比较内容),但s1 == s2是false(因为 比较引用地址,它们指向不同的堆内存对象)。
| 操作 | 描述 | 示例 | 内存行为 |
|---|---|---|---|
| 字面量赋值 | "str" |
String s1 = "Hello"; |
在字符串常量池中创建或查找对象,引用指向池中对象。 |
| 引用赋值 | s2 = s1; |
String s2 = s1; |
复制引用地址。s1 和 s2 指向同一个对象。 |
new 构造 |
new String("str") |
String s3 = new String("Hello"); |
强制在堆内存中创建一个新对象,引用指向堆中对象。 |
| 引用比较 | s1 == s2 |
比较两个引用变量是否指向同一个内存地址。 |
核心结论:
在 Java 中,String s2 = s1; 这条语句执行的是浅拷贝,即复制的是引用(内存地址),而不是字符串对象本身,这是理解 String 及其他对象赋值行为的基础。
