java参数传递始终是值传递,因为每次方法调用jvm都创建独立栈帧,局部变量表中基本类型存值、引用类型存地址值,实参到形参的传递本质是数值(含地址)的复制。

Java 中参数传递始终是值传递,这个结论的底层依据,就藏在方法调用时 JVM 创建的栈帧结构里,尤其是局部变量表的行为。
栈帧独立性决定参数互不干扰
每次方法调用,JVM 都会在当前线程的虚拟机栈中压入一个新栈帧。这个栈帧完全独立,拥有自己的局部变量表、操作数栈等。main 方法里的变量 a 和 modify 方法里的参数 b,分别存放在两个不同栈帧的局部变量表中——它们物理上就是两块不同的内存空间。
- main 栈帧的局部变量表索引 0 存的是 int a = 1(值本身)
- modify 栈帧的局部变量表索引 0 存的是形参 j(或 b),其值是 a 的一份拷贝,也是 1
- 修改 modify 栈帧中的 j,只改它自己那块内存,不影响 main 栈帧中 a 所在的位置
局部变量表如何承载“值传递”
局部变量表以变量槽(Slot)为单位存储数据。基本类型(如 int、boolean)直接把数值写入槽中;引用类型(如 Object、String)则把堆中对象的地址值写入槽中。关键点在于:这个“写入”,是实参值的一次复制动作。
- 调用 change(i) 时,JVM 把 main 栈帧中 i 槽里的整数值 1,读出来,再写进 change 栈帧中 j 槽里
- 调用 append(str) 时,JVM 把 main 栈帧中 str 槽里的地址值(比如 0x7f8a),复制一份,写进 append 栈帧中形参槽里
- 无论复制的是数字还是地址,都是“值”的拷贝,不是对原变量内存位置的绑定
引用类型容易误解的根源
当参数是对象引用时,传的是“指向对象的地址值”,所以两个栈帧里的引用变量初始时指向堆中同一个对象。但这不改变“值传递”的本质——因为地址本身被复制了,后续如果在被调方法中让形参指向新对象(如 str = new String("abc")),只是改了它自己槽里的地址值,main 栈帧中原来的引用槽仍保留旧地址。
- 修改对象内部状态(如 list.add(x))能生效,是因为两个引用都指向同一堆对象,操作的是共享数据
- 让形参指向新对象(str = "hello")不会影响实参,因为只是覆盖了形参所在栈帧的局部变量槽
- String 不可变 + 拼接会新建对象,正是这种机制的典型体现
调试时可观察的线索
通过 IDE 调试器单步进入方法,能清晰看到两个栈帧并存:调用栈窗口显示多层帧,变量视图中每个帧下有各自独立的变量列表。hover 查看变量时,基本类型显示值,引用类型显示类似 “String@12345678” 的哈希码——这个哈希码来自对象本身,不是变量槽的地址;而两个同名引用变量若哈希码相同,说明它们指向同一对象;若不同,则早已分道扬镳。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











