详解 Java 值传递与引用传递:用内存模型图还原参数传递的全过程
·
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 始终是值传递。对于引用类型,传递的是引用的值(即内存地址的拷贝),因此可通过地址修改对象,但无法直接操作原引用变量。
更多推荐



所有评论(0)