
核心结论先行Java 只有值传递没有引用传递。为了彻底搞懂这个问题需要先明确两个定义然后通过代码示例一步步验证。1. 先澄清两个关键概念值传递方法调用时实参将自己的值的副本传递给形参在方法内部修改形参不会影响实参本身。引用传递方法调用时实参将自己的内存地址直接传递给形参在方法内部修改形参指向的内容会直接影响实参指向的内容。关键区别在于值传递传递的是值的拷贝引用传递传递的是内存地址本身。2. Java 中的两种数据类型注意下面的措辞 —— 是引用类型不要看成引用传递。引用类型可以理解成一种基本属性或者标签引用传递可以理解成一种行为基本类型变量直接存储值本身如 int a 10 a 里面就是 10。引用类型变量存储的是对象的引用可以理解为内存地址真正的对象在堆内存中如 Person p new Person() p 里面存储的是指向 Person 对象的地址。3. 通过代码验证“只有值传递”3.1. 场景一传递基本类型基本类型有byte、short、int、long、float、double、char、booleanpublic class Test { public static void main(String[] args) { int num 10; System.out.println(调用前num num); // 输出 10 changeValue(num); System.out.println(调用后num num); // } public static void changeValue(int a) { a 20; // 修改的是形参 a 的副本 System.out.println(方法内部a a); // 输出 20 } }这里问号输出的结果是 10解释int 是基本类型所以 main 方法中的 num 变量存储的是10这个数值本身。调用changeValue(num)时JVM 将10这个值拷贝一份传给形参a。值传递现在内存中有两个独立的空间main 的 num 和 changeValue 的 a都存着10。在方法内修改a 20只是修改 a 的那个副本main 的 num 丝毫未受影响。结论基本类型肯定是值传递。3.2. 场景二传递引用类型引用类型有数组、对象这里直接看案例可以思考问号会是什么值class Person { String name; Person(String name) { this.name name; } } public class Test { public static void main(String[] args) { Person p1 new Person(张三); System.out.println(调用前p1.name p1.name); // 张三 changeName(p1); System.out.println(调用后p1.name p1.name); // } public static void changeName(Person p) { p.name 李四; // 通过引用修改了对象的属性 System.out.println(方法内部p.name p.name); // 李四 } }答案是李四。这是最容易误解的地方看到方法内修改了对象的属性外面也变了就认为这是引用传递。别急再看下面这个例子想想这里的问号是什么值public class Test { public static void main(String[] args) { Person p1 new Person(张三); System.out.println(调用前p1.name p1.name); // 张三 changeReference(p1); System.out.println(调用后p1.name p1.name); // } public static void changeReference(Person p) { p new Person(王五); // 让 p 指向一个新对象 System.out.println(方法内部p.name p.name); // 王五 } }答案是张三。看下面的运行截图所以如果 Java 支持“引用传递”那么这个案例中 main 中的 p1 应该也指向新对象“王五”。但结果它还是“张三”。形象解释main 方法中Person p1 new Person(张三)p1是一个变量想象它存储的是一个“地址”引用地址指向堆内存中Person(“张三”)这个对象。假设是p1变量的内容 0x123。调用changeReference(p1)时将p1中存储的地址0x123这个值拷贝一份赋值给形参p。现在p也存着 0x123也指向同一个对象。传递的是 0x123 这个数值而不是 p1 变量本身。这就是值传递所以这就是为什么第一个例子中修改p.name成功了。拟人表达就是因为p和p1都拿着同一个地址0x123。p 顺着地址去找到这个对象并且把对象的名字给改了改完之后当 p1 也顺着这个地址找到这个对象时发现名字就变了。第二个案例中在方法内执行p new Person(“王五”)。拟人表达就是p 把手上的地址坐标给改掉了然后拿着新地址去找那个地址对应的对象假设从0x123改为0x456新对象但 main 中的p1仍然拿着0x123纹丝不动。所以第二个案例最后输出的还是“张三”。结论引用类型也是值传递。4. 图示理解4.1. 修改对象属性p.name 李四初始状态main中p1 new Person(张三)调用changeName(p1)传递地址值的拷贝方法内执行p.name 李四通过任意引用修改对象结果p1.name变成李四因为两个变量指向同一个对象。4.2. 改变引用指向p new Person(王五)初始状态同上方法内执行p new Person(王五)改变形参的引用结果p1仍然指向对象Aname 张三形参p指向了新对象B。5. 最终结论Java 只有值传递没有引用传递。当参数是基本类型时传递的是数值字面量的拷贝。当参数是引用类型时传递的是引用的值地址编号的拷贝。