- 核心概念:Java 中“一切皆传值” (Call by Value)
- 两种情况分析:传递基本类型 vs. 传递引用类型
- “修改对象内容” vs. “重新引用对象”
- 实战示例与代码分析
- 总结与最佳实践
核心概念:Java 中“一切皆传值” (Call by Value)
首先要明确一个最根本的原则:Java 只有“传值调用”(Call by Value),没有“传引用调用”(Call by Reference)。

这里的“值”指的是什么?取决于你传递的参数类型:
- 如果参数是基本数据类型(如
int,double,char),传递的是该变量值的拷贝。 - 如果参数是引用数据类型(如对象、数组),传递的是该变量所指向的对象内存地址的拷贝。
很多人会混淆,因为传递对象时,看起来像是“传引用”,但实际上传递的是“地址的值”,这个细微差别是理解对象参数的关键。
两种情况分析
传递基本类型参数
这是最简单的情况,方法接收到的是原始值的一个副本,在方法内部对这个副本的任何修改,都不会影响到方法外部的原始变量。
示例代码:

public class PrimitiveParameter {
public static void main(String[] args) {
int number = 10;
System.out.println("调用方法前: number = " + number);
// 传递 number 的值 (10) 的拷贝给 modifyValue 方法
modifyValue(number);
System.out.println("调用方法后: number = " + number);
}
public static void modifyValue(int value) {
// 这里的 value 是 number 的一个副本
value = 20;
System.out.println("在方法内部修改后: value = " + value);
}
}
输出结果:
调用方法前: number = 10
在方法内部修改后: value = 20
调用方法后: number = 10
分析:
main 方法中的 number 变量和 modifyValue 方法中的 value 变量是两个完全独立的变量。modifyValue 方法内部只是修改了它自己的副本 value,main 方法中的原始 number 毫无影响。
传递对象参数
这是更复杂且容易出错的地方,当传递一个对象时,情况如下:
- 原始变量(如
myDog)存储的是对象的内存地址。 - 调用方法时,这个地址的拷贝被传递给了方法的参数(如
dog)。 - 方法内部的参数和原始变量指向了同一个内存中的同一个对象。
这意味着:

- 如果你通过这个参数去修改对象内部的状态(比如调用对象的 setter 方法),那么这个修改会反映到原始对象上,因为它们是同一个对象。
- 如果你在方法内部将这个参数重新指向一个全新的对象,那么这只会改变参数这个局部变量的指向,不会影响原始变量。
子情况 A:修改对象内部状态
示例代码:
class Dog {
private String name;
public Dog(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class ObjectParameterModify {
public static void main(String[] args) {
Dog myDog = new Dog("Buddy");
System.out.println("调用方法前: myDog 的名字是 " + myDog.getName());
// 传递 myDog 的地址的拷贝给 changeName 方法
changeName(myDog);
System.out.println("调用方法后: myDog 的名字是 " + myDog.getName());
}
public static void changeName(Dog dog) {
// dog 和 myDog 指向同一个 Dog 对象
// 通过 dog 修改对象内部状态,会影响 myDog 指向的对象
dog.setName("Charlie");
System.out.println("在方法内部修改后: dog 的名字是 " + dog.getName());
}
}
输出结果:
调用方法前: myDog 的名字是 Buddy
在方法内部修改后: dog 的名字是 Charlie
调用方法后: myDog 的名字是 Charlie
分析:
main 中的 myDog 和 changeName 方法中的 dog 参数都指向堆内存中同一个 Dog 对象,当 dog.setName("Charlie") 被执行时,它修改的是那个共享对象内部的 name 字段。main 方法中通过 myDog 再次访问时,看到的是修改后的结果。
子情况 B:重新引用对象
示例代码:
class Dog {
private String name;
public Dog(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class ObjectParameterReassign {
public static void main(String[] args) {
Dog myDog = new Dog("Buddy");
System.out.println("调用方法前: myDog 的名字是 " + myDog.getName());
// 传递 myDog 的地址的拷贝
reassignObject(myDog);
System.out.println("调用方法后: myDog 的名字是 " + myDog.getName());
}
public static void reassignObject(Dog dog) {
// dog 参数现在指向了一个全新的 Dog 对象
// 这不会影响 main 方法中的 myDog 变量
dog = new Dog("Charlie");
System.out.println("在方法内部重新引用后: dog 的名字是 " + dog.getName());
}
}
输出结果:
调用方法前: myDog 的名字是 Buddy
在方法内部重新引用后: dog 的名字是 Charlie
调用方法后: myDog 的名字是 Buddy
分析:
main方法中myDog指向一个名字为 "Buddy" 的对象。reassignObject方法被调用,dog参数接收了myDog地址的拷贝,dog和myDog都指向 "Buddy" 对象。- 在
reassignObject方法内部,dog = new Dog("Charlie");这行代码创建了一个新的Dog对象,并将dog这个局部变量的引用指向了这个新对象。 - 这个操作只改变了
dog的指向,而main方法中的myDog仍然指向原来的那个 "Buddy" 对象。myDog的名字没有改变。
“修改对象内容” vs. “重新引用对象”
为了更清晰地理解,我们可以用一个比喻:
- 对象变量(如
myDog):就像一张写着“地址”的纸条。 - 对象本身:就像那个地址对应的一栋房子。
| 操作 | 描述 | 结果 |
|---|---|---|
| 传递对象参数 | 把写着“地址”的纸条复印一份,交给方法。 | 方法里的纸条和你的纸条指向同一个地址(同一栋房子)。 |
| 修改对象内容 | 通过方法里的纸条找到房子,然后进去把墙漆成蓝色。 | 你手里的纸条指向的房子也变成了蓝色。影响原始对象。 |
| 重新引用对象 | 方法里把那张纸条扔掉,写上一个新地址,指向另一栋新房子。 | 你手里的纸条没变,它仍然指向原来的那栋老房子。不影响原始对象。 |
如何在方法中修改原始对象本身?
如果你确实需要在方法内部创建一个新对象,并让方法外部的原始变量也指向这个新对象,那么你需要传递一个可变引用,在 Java 中,这意味着传递一个对象的数组或容器类(如 AtomicReference)。
示例:使用数组
public class MutableReference {
public static void main(String[] args) {
// 使用一个数组来包装我们的对象
Dog[] myDogArray = new Dog[]{new Dog("Buddy")};
System.out.println("调用方法前: myDog[0] 的名字是 " + myDogArray[0].getName());
changeReference(myDogArray);
System.out.println("调用方法后: myDog[0] 的名字是 " + myDogArray[0].getName());
}
public static void changeReference(Dog[] dogArray) {
// dogArray[0] 是一个可变的引用,可以给它赋一个新对象
dogArray[0] = new Dog("Charlie");
}
}
// Dog 类同上
输出结果:
调用方法前: myDog[0] 的名字是 Buddy
调用方法后: myDog[0] 的名字是 Charlie
分析:
这里我们传递的是一个数组 myDogArray,数组本身是对象,它的元素也是引用。changeReference 方法可以修改数组中的引用 dogArray[0],让它指向一个新对象,因为 main 方法中的 myDogArray 和 changeReference 中的 dogArray 是同一个数组对象,所以对 dogArray[0] 的修改会影响到 myDogArray[0]。
总结与最佳实践
| 参数类型 | 传递方式 | 方法内修改对原始变量/对象的影响 | 常见场景 |
|---|---|---|---|
基本类型 (int, double 等) |
传值(值的拷贝) | 无影响,方法内修改的是副本。 | 计算并返回一个结果。 |
对象引用 (Dog, List 等) |
传值(地址的拷贝) | 影响对象内容(如果修改对象内部状态)。 不影响引用本身(如果重新指向新对象)。 |
修改对象状态、执行对象方法。 |
可变引用 (数组, AtomicReference) |
传值(地址的拷贝) | 可以影响引用本身,使其指向新对象。 | 当方法需要“返回”一个新对象并替换掉原始引用时。 |
核心要点:
- Java 总是传值,但这个“值”对于对象来说是地址。
- 当你传递一个对象给方法时,你传递的是该对象地址的拷贝。
- 这使得方法可以访问和修改原始对象的状态。
- 但这并不让方法能够替换掉原始变量所引用的对象。
- 如果需要在方法中替换原始引用,请传递数组或容器类。
理解了这一点,你就能在 Java 编程中自信地处理对象参数,避免许多常见的逻辑错误。
