昨天有个读者私信我,说自己面试时被问到“Java参数传递到底是值传递还是引用传递”,他张口就回答“引用传递”,还自信满满地补了一句“因为对象传的是引用”。结果面试官让他现场写个交换两个对象的swap方法,他当场愣住了。这种场景我见得太多了,因为这道题表面上就八个字,但几乎有一半以上的Java开发者都会答错,或者答不到点子上。
我最早入行时也栽过这个跟头。当时照着某本教材上的说法背“基本类型是值传递,对象是引用传递”,自我感觉良好。后来被人追问“那为什么方法里给参数重新new一个对象,外面没变?”,我瞬间不知道怎么解释。也就是从那时起,我才开始认真去看JVM的内存模型,去看Java到底怎么处理形参和实参的关系。这篇文章就把我这些年反复梳理的结论、实验、面试话术和实际开发里的教训一次讲清楚。
1. 一个面试场景引发的争论:结论不是“背”出来的
1.1 为什么这道题能成为面试高频题
你打开任何一份Java面试题库,“值传递还是引用传递”基本都是前排常客。它之所以被反复问,不是面试官闲得慌,而是这道题天然适合用来区分“背答案的人”和“真正理解底层的人”。
很多人背的结论是:基本类型传值,对象传引用。这个简化的说法在笔试选择题里能蒙对方向,但只要面试官往深了追问一句“你刚才说的引用,是Java里的对象引用,还是C++里的引用语义”,大部分人就会露馅。因为这两种“引用”根本不是一个东西。
C++里的引用传递,形参是实参的别名,你在函数里给形参重新赋值,外面那个变量也一起变。Java里没有这种机制。Java里的对象引用,本质上只是一个存放对象地址的变量,它被复制过去的时候,复制的是地址值本身。所以严格从参数传递的语义来说,Java里只有值传递,没有引用传递。
1.2 先给一个经得起推敲的标准结论
我把结论写在这里,你先记住,后面再用代码验证:
Java方法的参数传递是值传递。
- 如果参数是基本类型,传递的是值的副本。方法内修改形参,实参不受影响。
- 如果参数是引用类型,传递的是引用地址的副本。方法内对形参重新赋值(比如指向一个新对象),实参不受影响;但通过形参修改它指向的那个对象的内容,实参变量指向的对象会受影响。
也就是说,无论参数是什么类型,方法拿到的都是“实参变量里那个值的复印件”。区别只在于:基本类型复印件里装的是数值,引用类型复印件里装的是对象地址。你拿着复印件改不了原件上的数字,但如果复印件和原件写的是同一个储物柜编号,你把储物柜里的东西换了,别人打开同一个柜子时看到的自然也就是你放进去的新东西。
这个比喻很关键,它解释了后面所有实验现象。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 栈帧里的真相:参数传递在底层到底做了什么
2.1 方法调用时JVM做了什么
要真正理解值传递,不能只看源代码层,得往下看一层:JVM的方法调用机制。
Java的方法调用是基于虚拟机栈的。每次调用一个方法,JVM就会在当前线程的虚拟机栈里创建一个栈帧。局部变量表、操作数栈、动态链接、方法出口这些信息都存在这个栈帧里。其中局部变量表就是我们说的“变量”真正存放的地方,它是以槽为单位的,一个槽可以装一个int、一个引用地址,long和double这种64位的类型会占两个槽。
当你执行 int b = add(a);,在进入add方法的那一刻,JVM会为add方法创建新栈帧,并把你传入的实参值复制到新栈帧的局部变量表里。注意“复制”这个动作,它是值传递的物理基础。实参和形参,分别在两个不同的栈帧里,各自占各自的槽位。形参不是实参的引用,它就是一个独立的副本变量。
这个机制解释了为什么在方法内给形参赋值,外面的实参根本感知不到——因为它们是两个栈帧里的两个独立槽位,操作方法内的那个槽位,当然影响不了方法外的那个槽位。
2.2 “引用也是一种值”是理解整道题的关键
很多人卡在“引用传递”这四个字上,是因为混淆了两个层面的“引用”。
第一个层面:Java语言里的“引用类型”,指的是除基本类型之外的所有类型,包括类、接口、数组、枚举。一个引用类型变量里存储的并不是对象本身,而是对象的地址(或者说“指向”)。这是Java隔离栈和堆的一种设计。
第二个层面:参数传递里的“引用传递”,是一种传参机制,它要求形参直接绑定到实参变量上,操作形参就是操作实参本身。Java没有这种机制。
所以当你听到“对象传的是引用”,这句话本身没错,因为对象参数变量里装的就是引用地址。但是“传引用”不等于“引用传递”。Java把引用地址复制了一份再传过去,这个复制出来的地址又是值,所以从整体上,参数传递的机制仍然是值传递。
我记得当年把这些概念彻底捋清楚的瞬间,是在JDK源码里看到一句话:Java把“references to objects are passed by value”。这句话比各种博客总结都准确:对象引用以值的方式传递。你要非用一句极简的话概括整个Java
