杰瑞科技汇

Java String赋值是引用传递还是值传递?

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

Java String赋值是引用传递还是值传递?-图1
(图片来源网络,侵删)

下面我们来详细分解这个过程。

核心概念:String 不可变性与引用

要理解两个关键点:

  1. String 的不可变性:在 Java 中,String 对象一旦被创建,其内容(值)就不能被改变,任何对字符串的修改操作(如 concat(), replace() 等)实际上都是创建了一个新的 String 对象,而不是修改原来的对象。
  2. 引用类型String 是一个引用类型,当你声明一个 String 变量时,你实际上是在创建一个引用(可以理解为指针或地址),它指向内存中存储字符串数据的地方。
String str1; // 只声明了一个引用变量 str1,它目前不指向任何对象
str1 = "Hello"; // 创建了一个 "Hello" 对象,str1 引用指向它

赋值操作:str2 = str1; 的过程

让我们通过一个具体的例子来理解赋值过程。

String str1 = "Hello";
String str2 = str1; // 关键步骤:将 str1 赋值给 str2

这个过程发生了什么?

Java String赋值是引用传递还是值传递?-图2
(图片来源网络,侵删)
  1. 创建 str1

    • JVM 在字符串常量池中查找是否存在 "Hello" 这个字符串。
    • 如果不存在,就在池中创建一个新的 "Hello" 对象。
    • 如果已经存在,就直接复用这个对象。
    • 变量 str1 被创建,它的值是池中 "Hello" 对象的内存地址。
  2. 赋值 str2 = str1

    • 这行代码并没有在字符串常量池中创建新的 "Hello" 对象。
    • 它执行的是引用复制:将变量 str1 中存储的内存地址复制一份,然后赋给变量 str2
    • str1str2 这两个变量都指向了内存中同一个 "Hello" 对象。

内存示意图:

      +-------------------+
      |  字符串常量池      |
      +-------------------+
      |                   |
      |   "Hello" 对象     | <--- 内存地址 0x1234
      |                   |
      +-------------------+
             ^
             |
+----------------+      +----------------+
|  str1 变量      |      |  str2 变量      |
| (引用/地址)     |----->| (引用/地址)     |
|      0x1234     |      |      0x1234     |
+----------------+      +----------------+

代码示例与验证

让我们通过代码来验证这个行为。

Java String赋值是引用传递还是值传递?-图3
(图片来源网络,侵删)
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

这个例子清晰地证明了:

  • 赋值是引用的传递,str1str2 一开始指向同一个对象。
  • 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 == s2false(因为 比较引用地址,它们指向不同的堆内存对象)。
操作 描述 示例 内存行为
字面量赋值 "str" String s1 = "Hello"; 在字符串常量池中创建或查找对象,引用指向池中对象。
引用赋值 s2 = s1; String s2 = s1; 复制引用地址s1s2 指向同一个对象。
new 构造 new String("str") String s3 = new String("Hello"); 强制在堆内存中创建一个新对象,引用指向堆中对象。
引用比较 s1 == s2 比较两个引用变量是否指向同一个内存地址

核心结论:

在 Java 中,String s2 = s1; 这条语句执行的是浅拷贝,即复制的是引用(内存地址),而不是字符串对象本身,这是理解 String 及其他对象赋值行为的基础。

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