Java只有值传递,包括对象类型;传递的是引用变量的值(内存地址副本),因此修改对象内容会影响外部,但重新赋值形参不影响实参。

Java 里根本没有“引用传递”,只有值传递
Java 所有参数传递都是值传递——包括 Object 类型。所谓“对象被修改了”,不是因为传了引用,而是因为你传的是那个引用变量的值(即内存地址),而这个值本身被复制了一份。你操作的是副本指向的同一块堆内存,所以看起来像“引用传递”。
常见错误现象:swap() 方法无法交换两个对象引用;String 在方法内重新赋值不影响外部变量;但 ArrayList.add() 却能影响外部列表。
-
String、Integer等不可变类型:方法内s = "new"只改本地副本,原引用不变 - 可变对象(如
ArrayList、自定义类):方法内调用list.add()是在堆上修改内容,外部可见 - 想真正“交换引用”?必须返回新引用或用容器包装(如
AtomicReference)
怎么验证传的是“引用的值”,而不是“引用本身”?
关键看变量本身的值能不能被函数内部改写并影响外部——不能。可以用一个简单的 reassign() 测试:
public static void reassign(List<String> list) {
list = new ArrayList<>(); // 这行只改了形参 list 的值(地址)
list.add("inside");
}
public static void main(String[] args) {
List<String> l = new ArrayList<>();
l.add("before");
reassign(l);
System.out.println(l.size()); // 输出 1,不是 0
}
说明:list 是个局部变量,存的是地址(比如 0x1a2b)。调用时把 0x1a2b 复制给了形参。函数内 list = new ArrayList<>() 是把形参改成了另一个地址(比如 0x3c4d),原始变量仍指向 0x1a2b。
立即学习“Java免费学习笔记(深入)”;
为什么 String 和 ArrayList 表现不同?
差异不在传递方式,而在对象是否可变、以及你操作的是“引用值”还是“引用指向的对象内容”:
-
String不可变:所有“修改”操作(如substring()、toUpperCase())都返回新对象,原引用不变 -
ArrayList可变:add()、set()直接改堆内存里的数组内容,不改变引用值本身 - 如果对
ArrayList做list = new ArrayList<>(),同样不会影响外部——和String一样
实际开发中容易踩的坑
最常被忽略的是:你以为在“传对象”,其实只是在传一个地址拷贝;一旦重新赋值,就断开了和原始变量的联系。
- 误以为
method(obj)后obj会变成null或新对象——除非你在方法里显式返回并重新赋值 - 在循环里反复
list = getListFromDB(),却忘了清空旧引用,导致内存泄漏(尤其搭配静态缓存时) - 多线程下共享可变对象(如
HashMap),没加同步,以为“传进去了就安全了”——传的是地址值,大家操作同一块堆内存 - 调试时打印
System.identityHashCode(obj),比对前后是否一致,是判断是否指向同一对象的可靠方式
真正的复杂点从来不是“传递机制”,而是你有没有意识到:变量名只是栈上的一个标签,它背后那个地址值,既可能指向可变内容,也可能被悄悄覆盖掉。








