Java 值传递与引用传递详解

Java 的参数传递机制是严格的值传递,但根据数据类型不同(基本类型/引用类型),内存行为存在差异。以下通过内存模型图逐步还原全过程:


1. 基本数据类型:纯值传递

内存模型图

调用前栈帧:
[main]  a=10 (地址0x100)

调用时复制:
[swap]  x=10 (地址0x200)  // 拷贝a的值
[swap]  y=20 (地址0x300)  // 拷贝b的值

交换后:
[swap]  x=20, y=10  // 仅修改副本

返回main栈帧:
[main]  a=10, b=20  // 原值未变

代码验证

public static void main(String[] args) {
    int a = 10, b = 20;
    swap(a, b);  // 传递a和b的拷贝值
    System.out.println("a=" + a + ", b=" + b);  // 输出: a=10, b=20
}

static void swap(int x, int y) {
    int temp = x;
    x = y;
    y = temp;  // 仅修改副本
}

关键点

  • 传递基本类型时,直接复制数值本身到新栈帧。
  • 方法内操作的是独立副本,原变量不受影响。

2. 引用数据类型:传递引用的值

内存模型图

调用前堆内存:
[0x100] Person{name="Alice"}

调用前栈帧:
[main]  p1 → 0x100 (箭头指向堆地址)

调用时复制引用值:
[changeName]  p → 0x100  // 拷贝p1的地址值

修改堆内对象:
[0x100] Person{name="Bob"}  // 通过地址修改对象

返回main栈帧:
[main]  p1 → 0x100 → "Bob"  // 原对象被修改

代码验证

class Person {
    String name;
    Person(String name) { this.name = name; }
}

public static void main(String[] args) {
    Person p1 = new Person("Alice");
    changeName(p1);  // 传递p1引用的拷贝值(地址0x100)
    System.out.println(p1.name);  // 输出: Bob
}

static void changeName(Person p) {
    p.name = "Bob";  // 通过地址修改堆内对象
}

关键点

  • 传递引用类型时,复制的是引用的值(即对象地址),而非对象本身。
  • 方法内通过地址可修改堆内对象内容,但无法修改原引用指向(见下例)。

3. 引用不可变性的验证

内存模型图

调用前堆内存:
[0x100] Person{name="Alice"}
[0x200] Person{name="Bob"}

调用前栈帧:
[main]  p1 → 0x100

调用时复制引用值:
[reassign]  p → 0x100  // 拷贝地址

尝试重定向引用:
[reassign]  p → 0x200  // 仅修改副本地址

返回main栈帧:
[main]  p1 → 0x100 → "Alice"  // 原引用未变

代码验证

public static void main(String[] args) {
    Person p1 = new Person("Alice");
    reassign(p1);  // 传递地址拷贝
    System.out.println(p1.name);  // 输出: Alice(未变)
}

static void reassign(Person p) {
    p = new Person("Bob");  // 仅修改副本的指向
}

关键点

  • 方法内对引用重新赋值(p = new...),只影响副本的指向,原引用不变。
  • 这再次证明 Java 是值传递:传递的是地址值,而非引用本身。

总结:Java 参数传递规则

类型 传递内容 是否影响原数据
基本数据类型 数值的拷贝 ❌ 不影响原变量
引用数据类型 地址值的拷贝 ✅ 可修改堆内对象内容
❌ 不可修改原引用指向

核心结论

Java 始终是值传递。对于引用类型,传递的是引用的值(即内存地址的拷贝),因此可通过地址修改对象,但无法直接操作原引用变量。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐