java只有值传递:基本类型传值副本,修改不影响原变量;引用类型传地址副本,可修改对象内容但无法改变原引用指向;重赋值形参仅改变副本地址,实参引用不变。

Java 中只有值传递,这是语言规范明确规定的本质特性。所谓“证明”,不是靠理论背诵,而是用三段原生、无依赖、可直接运行的代码,分别击穿基本类型、普通对象、以及“看似能改引用”的典型误区——每段都带清晰输出和关键注释,一眼看清内存行为。
基本类型:副本改不动原件
以下代码中,main 里的 num 和 swap 方法里的 a 是两个独立栈变量:
public static void main(String[] args) {int num = 10;
System.out.println("调用前: " + num); // 输出 10
swap(num);
System.out.println("调用后: " + num); // 仍输出 10
}
public static void swap(int a) {
a = 999;
System.out.println("方法内: " + a); // 输出 999
}
说明:
• num 存在栈中,值为 10;
• 调用时,JVM 把这个 10 复制一份给形参 a,a 也在栈中,但地址不同;
• 修改 a 只动副本,num 完全不受影响。
引用类型:传的是地址副本,不是对象本身
下面这段代码常被误读为“引用传递”,实则恰恰印证了“值传递”——传的是引用(即内存地址)的副本:
StringBuilder sb = new StringBuilder("hello");
System.out.println("调用前: " + sb.toString()); // hello
appendWorld(sb);
System.out.println("调用后: " + sb.toString()); // hello world
}
public static void appendWorld(StringBuilder s) {
s.append(" world"); // ✅ 修改堆中对象的内容
System.out.println("方法内: " + s.toString()); // hello world
}
关键点:
• sb 和 s 都是栈中的变量,各自存着同一个堆地址(比如 0x1a2b);
• s.append(...) 没有改变 s 自身的值(它还是 0x1a2b),只是通过该地址去修改堆里那个 StringBuilder 对象的内部 char 数组;
• 所以外部看到“变了”,变的是堆对象,不是栈变量本身——这仍是值传递下的合法行为。
破除幻觉:重赋值形参 ≠ 改变实参
这才是最有力的“红线验证”。只要在方法内让形参指向新对象,就能立刻暴露“副本地址”本质:
public static void main(String[] args) {List
list.add("A");
System.out.println("调用前 size: " + list.size()); // 1
reassign(list);
System.out.println("调用后 size: " + list.size()); // 还是 1!
}
public static void reassign(List
l = new ArrayList(); // ❗把形参 l 指向一个全新堆对象
l.add("X");
System.out.println("方法内 size: " + l.size()); // 1
}
为什么外部 list 没变?
• list 在栈中存的是旧 ArrayList 的地址(如 0x3c4d);
• l = new ArrayList() 是把 l 这个栈变量的值从 0x3c4d 改成新地址(如 0x5e6f),仅此而已;
• list 的值(0x3c4d)全程没被触碰过,它指向的旧对象也依然存在。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











