Java对象参数传递内存机制解惑:测试代码异常解析请求
Java对象作为方法参数的内存处理逻辑解析
首先得明确一个核心点:Java的参数传递永远是「值传递」——不管你传的是基本类型还是对象引用,方法接收的都是传入值的一个副本。当参数是对象类型时,传递的是「对象引用(内存地址)的副本」,这就是你看到不同结果的根源。咱们结合你的测试代码逐一拆解:
测试代码回顾
public class TestCode { public static void main(String[] args) { TestCode obj = new TestCode(); CustomClass cs1 = new CustomClass(5); obj.updateExistingObj(cs1); System.out.println(cs1.val); // 输出9 CustomClass cs2 = new CustomClass(5); obj.instantiateExistingObj(cs2); System.out.println(cs2.val); // 输出5 CustomClass cs3 = null; obj.updateNullObj(cs3); System.out.println(cs3.val); // 空指针异常 } void updateExistingObj(CustomClass cs1) { cs1.val = 9; } void instantiateExistingObj(CustomClass cs2) { cs2 = new CustomClass(9); } void updateNullObj(CustomClass cs3) { cs3 = new CustomClass(9); } } class CustomClass { int val; CustomClass next; CustomClass(int x) { val = x; } }
逐个场景解析
1. updateExistingObj方法:修改对象属性,原引用看到变化
- 当你把
cs1传入方法时,方法的参数cs1是原引用的副本——它和外部的cs1指向堆内存中同一个CustomClass对象(就是那个val=5的实例)。 - 方法里执行
cs1.val = 9,是通过这个副本引用找到堆里的对象,修改了对象的val属性。因为原引用和副本引用指向同一个对象,所以外部打印cs1.val时,看到的就是修改后的9。
2. instantiateExistingObj方法:给副本引用赋值,原引用不受影响
- 一开始,方法参数
cs2是外部cs2的引用副本,两者指向同一个val=5的对象。 - 但方法里执行
cs2 = new CustomClass(9)时,是让方法内部的副本引用指向了一个全新的CustomClass对象(val=9)。这时候外部的cs2还是牢牢指向原来那个val=5的对象,完全不受副本引用的影响。所以外部打印cs2.val还是5。 - 划重点:这里修改的是「引用本身」,而不是「引用指向的对象的属性」,所以原引用不会有任何变化。
3. updateNullObj方法:副本引用新建对象,原引用还是null
- 外部的
cs3是null,意味着它没有指向任何堆内存对象。传入方法后,方法参数cs3是这个null的副本。 - 方法里
cs3 = new CustomClass(9),只是让方法内部的副本引用指向了新对象,但外部的cs3依然是null。当你在外部调用cs3.val时,null没有对应的对象,自然会抛出NullPointerException。
核心总结
- Java不存在「引用传递」,只有值传递:引用类型传递的是「引用地址的副本」。
- 如果通过副本引用修改对象的属性,会影响原对象(因为指向同一个堆实例)。
- 如果直接给副本引用赋值(比如
new新对象、指向其他对象),只会改变副本的指向,和原引用、原对象都没关系。
内容的提问来源于stack exchange,提问作者Rito
相关产品推荐
相关产品推荐

