方法参数传递:Java 到底是不是值传递
更新时间:2026-09-20。这是 Java 面试与排障的高频争议题。结论先行:Java 只有值传递(pass by value),只是「引用类型传的值,是对象引用的副本」。配合数组共享堆内存一起理解最清楚。
一、两个概念先对齐
| 术语 | 含义 |
|---|---|
| 值传递 | 调用方法时,把实参的副本交给形参,方法内改的是副本 |
| 引用传递 | 把实参变量本身的地址交进去,方法内能让外部变量指向别处 |
Java 对两种类型都传「值」:基本类型传值的副本,引用类型传引用(地址)的副本。副本与原件指向同一个对象,但它们是两根独立的「线」。
二、基本类型:改了也白改
java
static void reset(int x) { x = 0; }
int n = 10;
reset(n);
System.out.println(n); // 仍是 10n 把 10 复制一份给形参 x,x=0 只动了栈上的副本,外部的 n 毫发无损。
三、引用类型:能改内容,不能换对象
java
static void rename(User u) { u.name = "李四"; } // ① 改字段:外部可见
static void replace(User u) { u = new User("王五"); } // ② 重新指向:外部无感
User p = new User("张三");
rename(p); // p.name 变成 "李四"
replace(p); // p 仍是那个 "李四",不会变成王五
u和p是两根引用,但初始指向同一个堆对象,所以通过u改字段,p看得到。u = new User(...)只是把副本这根线接到新对象,外部那根p还连着旧对象。
四、经典反例:swap 为什么失败
java
static void swap(Integer a, Integer b) {
Integer t = a; a = b; b = t; // 只交换了两个局部副本
}
Integer x = 1, y = 2;
swap(x, y);
// x 仍是 1,y 仍是 2如果 Java 是引用传递,swap 内部对形参的重绑定会反映到外部,外部 x/y 就该互换;事实是没有,这正证明了「传的是副本」。要在方法外得到结果,用返回值、改对象字段/数组元素或持有容器。
坑
- 以为把集合传进方法、方法内重新赋值能清空外部集合:
list = new ArrayList<>()无效;要生效应调list.clear()。 - 可变对象被多个引用共享:把同一个 List 传进好几个方法,任一处 add/remove 都会互相影响,注意防御式拷贝。
- 包装类/ String 的「看似没改」:它们不可变,
u = u + 1、s = s + "x"都是生成新对象再让副本指向它,外部自然不变——别误判成「基本类型才是值传递」。 - 想交换两个变量:Java 做不到按引用 swap,需返回数组/对象或用数组下标交换。
总结
Java 严格只有值传递:基本类型传的是数据值的副本,方法内修改不影响外部;引用类型传的是对象地址的副本,形参与实参是两根指向同一堆对象的独立引用,因此能通过副本修改对象字段、却无法让外部引用改指新对象——swap 失效、方法内重新赋值外部不变都是这个原因,需要带出结果就用返回值或修改共享对象本身。 引用与对象的存储见数组内存模型与JVM 栈与堆。