这是 Java 基础中一个非常经典且容易混淆的概念,也是面试中必考的知识点。
1. 核心结论:Java 中只有值传递 (Pass by Value)
无论方法参数是基本数据类型还是引用数据类型,Java 在方法调用时,总是将**实际参数的一个副本(一份拷贝)**传递给形式参数。
2. 什么是值传递?(以及基本数据类型的情况)
- 定义:方法接收到的是实际参数的值的副本。在方法内部对这个副本的任何修改,都不会影响到方法外部的实际参数。
- 基本数据类型示例:
public class PassByValueDemo {public static void changePrimitive(int num) {System.out.println("进入方法前,num = " + num); // num = 10num = 20; // 修改的是 num 的副本System.out.println("退出方法前,num = " + num); // num = 20 }public static void main(String[] args) {int originalNum = 10;System.out.println("调用方法前,originalNum = " + originalNum); // originalNum = 10 changePrimitive(originalNum);System.out.println("调用方法后,originalNum = " + originalNum); // originalNum = 10 } }
- 解释:当
changePrimitive方法被调用时,originalNum的值10被复制一份,传递给了num。num和originalNum拥有各自独立的内存空间。所以在方法内部修改num的值,对originalNum没有任何影响。
3. 引用数据类型的传递:依然是值传递 (传递的是引用的副本)
这是最容易产生误解的地方。很多人会认为引用数据类型是“引用传递”,但实际上,Java 传递的依然是引用地址的副本。
- 核心:传递给方法的是对象内存地址的一个副本。这意味着方法内部和外部的引用变量虽然指向了同一个对象,但它们本身是两个独立的引用变量。
- 引用数据类型示例:
class MyObject {String value;public MyObject(String value) {this.value = value;} }public class PassByReferenceLookalikeDemo {public static void changeObjectValue(MyObject obj) {System.out.println("进入方法前,obj.value = " + obj.value); // obj.value = "Original"obj.value = "Changed"; // 通过副本引用修改了同一个对象内部的状态System.out.println("退出方法前,obj.value = " + obj.value); // obj.value = "Changed" }public static void swapObjects(MyObject obj1, MyObject obj2) {System.out.println("进入方法前,obj1.value = " + obj1.value + ", obj2.value = " + obj2.value);MyObject temp = obj1;obj1 = obj2; // obj1 的副本指向了 obj2 副本指向的对象obj2 = temp; // obj2 的副本指向了 obj1 副本最初指向的对象System.out.println("退出方法前(方法内部),obj1.value = " + obj1.value + ", obj2.value = " + obj2.value);}public static void main(String[] args) {MyObject myObj = new MyObject("Original");System.out.println("调用 changeObjectValue 前,myObj.value = " + myObj.value); // myObj.value = "Original" changeObjectValue(myObj);System.out.println("调用 changeObjectValue 后,myObj.value = " + myObj.value); // myObj.value = "Changed"// 结果显示对象内部状态被修改了,这让人误以为是引用传递 System.out.println("\n--- 证明是值传递的经典案例:交换引用 ---");MyObject a = new MyObject("A");MyObject b = new MyObject("B");System.out.println("调用 swapObjects 前,a.value = " + a.value + ", b.value = " + b.value); // A, B swapObjects(a, b);System.out.println("调用 swapObjects 后,a.value = " + a.value + ", b.value = " + b.value); // A, B// 结果显示外部的 a 和 b 引用并没有被交换,证明传递的是引用的副本 } }
- 解释:
- 当
changeObjectValue方法被调用时,myObj存储的内存地址被复制一份,传递给了obj。此时,myObj和obj都是独立的引用变量,但它们都指向堆中同一个MyObject对象。
- 在方法内部,通过
obj.value = "Changed";这种操作,我们是修改了obj指向的那个对象内部的value字段。由于myObj也指向同一个对象,所以从myObj来看,对象的内部状态被改变了,这看起来很像“引用传递”。
- 但通过
swapObjects示例,可以清晰地证明是值传递。在方法内部,obj1和obj2的副本被交换了,但方法外部的a和b原始引用并未受到影响,它们仍然指向最初的对象。这说明,方法改变的只是它自己接收到的引用副本的指向,而非外部原始引用的指向。
4. 总结
Java 中只有值传递 (Pass by Value):
- 基本数据类型:传递的是值的副本。
- 引用数据类型:传递的是引用地址的副本。虽然方法内可以通过引用副本修改对象内部状态,但如果修改引用副本本身的指向(例如在方法内
obj = null或obj = new AnotherObject()),不会影响外部的原始引用。
- 解释:当