目录
一、什么是“值传递”与“引用传递”?
值传递(Pass-by-Value)
引用传递(Pass-by-Reference)
二、Java 的真相:一切都是值传递
关键理解:
三、代码演示:为什么说 Java 是值传递?
场景1:基本类型(int)
场景2:对象引用(List)
场景3:重新赋值引用(关键测试!)
四、图解:内存模型视角
五、常见误区澄清
误区1:“对象是引用传递”
误区2:“能修改对象内容 = 引用传递”
六、如何真正“修改”调用方的引用?
方案1:返回新对象(推荐)
方案2:使用包装类(不推荐,复杂)
方案3:操作原对象内容
“Java 中,基本类型是值传递,对象是引用传递” —— 这是一个流传甚广的误解。
事实上,Java 中所有参数传递都是值传递(Pass-by-Value),包括对象。
本文将通过原理剖析、代码示例和常见误区澄清,彻底讲清楚 Java 的参数传递机制。
一、什么是“值传递”与“引用传递”?
值传递(Pass-by-Value)
- 调用函数时,将实参的值复制一份传给形参。
- 函数内部对形参的任何修改,不会影响原始实参。
- C 语言中基本类型就是典型的值传递。
引用传递(Pass-by-Reference)
- 调用函数时,直接将实参的内存地址(引用)传给形参。
- 函数内部对形参的修改,会直接影响原始实参。
- C++ 中的
&引用参数就是引用传递。
注意:Java 没有引用传递!
二、Java 的真相:一切都是值传递
Java 的设计哲学非常明确:
“Java manipulates objects ‘by reference,’ but it passes references to methods ‘by value.’”
——《Thinking in Java》
翻译:
“Java 通过引用来操作对象,但向方法传递引用时,是按值传递的。”
关键理解:
- 对象本身存储在堆内存中。
- 变量(如
List<String> list)不是对象本身,而是指向对象的引用(可理解为地址)。 - 当你把
list传给方法时,传递的是这个“地址”的副本,而不是地址本身。
三、代码演示:为什么说 Java 是值传递?
场景1:基本类型(int)
public static void main(String[] args) { int x = 10; changeInt(x); System.out.println(x); // 输出:10(未改变) } static void changeInt(int num) { num = 20; // 修改的是副本 }符合值传递:副本修改不影响原值。
场景2:对象引用(List)
public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("A"); changeList(list); System.out.println(list); // 输出:[A, B] ← 内容变了! } static void changeList(List<String> param) { param.add("B"); // 修改对象内容 }看起来像“引用传递”?其实不是!
list和param都指向同一个 ArrayList 对象。param.add("B")是修改对象的内容,不是修改引用。- 这属于“通过引用修改对象”,不是“引用传递”。
场景3:重新赋值引用(关键测试!)
public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("A"); reassignList(list); System.out.println(list); // 输出:[A] ← 完全没变! } static void reassignList(List<String> param) { param = new ArrayList<>(); // 让 param 指向新对象 param.add("X"); param.add("Y"); }🔥这才是判断是否为引用传递的关键!
- 如果是引用传递,
list应该变成[X, Y]。 - 但实际输出仍是
[A],说明param = ...只改变了局部变量的指向,不影响调用方。
这证明了:Java 传递的是引用的值(即地址的副本),不是引用本身。
四、图解:内存模型视角
五、常见误区澄清
误区1:“对象是引用传递”
- 错!对象本身不能被传递,传递的是指向对象的引用的副本。
- 正确说法:“Java 通过值传递引用”。
误区2:“能修改对象内容 = 引用传递”
- 错!能修改内容是因为多个引用指向同一个对象,与传递方式无关。
- 即使是值传递,只要共享同一个对象,就能互相看到修改。
六、如何真正“修改”调用方的引用?
虽然不能通过赋值改变调用方引用,但有替代方案:
方案1:返回新对象(推荐)
List<String> newList = createNewList(); original = newList; // 显式赋值方案2:使用包装类(不推荐,复杂)
class Ref<T> { T value; } void swap(Ref<List<String>> ref) { ref.value = new ArrayList<>(); }方案3:操作原对象内容
list.clear(); list.addAll(newData);