Java对象传入方法置空后仍可调用方法不抛空指针的原因
问题复现代码
public class SetClassNullExperiment { public static void main(String[] args) { SetMeNull c1 = new SetMeNull(); c1.setInputToNull(c1); System.out.println("Is c1 equal to null? " + Boolean.toString(c1 == null)); // 输出false c1.printHelloWorld(); SetMeNull c2 = new SetMeNull(); c2 = null; System.out.println("Is c2 equal to null? " + Boolean.toString(c2 == null)); // 输出true c2.printHelloWorld(); // 抛出NullPointerException } } class SetMeNull { /** * 尝试将传入的对象引用设为null */ public void setInputToNull(SetMeNull c) { c = null; } public void printHelloWorld() { System.out.println("Hello World!"); } }
底层原理
两组操作逻辑并不等价,核心原因是Java所有参数传递均为值传递,不存在引用传递:
- 调用
c1.setInputToNull(c1)时,方法形参c拿到的是c1变量存储值的副本——也就是c1指向的堆内存对象地址的拷贝,和main方法中的原始变量c1是两个完全独立的变量,只是初始阶段存了同一个地址值,指向同一个对象。 - 方法内执行
c = null,只是把形参这个临时变量存储的地址值改成了null,切断了形参和原对象的关联,完全不会改动main方法栈帧中c1本身存储的值。c1依旧指向最开始创建的SetMeNull实例,因此调用实例方法可以正常执行,不会抛出空指针。 - 直接执行
c2 = null时,修改的是main方法栈帧中c2变量本身的存储值,把原本存的对象地址直接替换为null,此时c2不再指向任何有效的堆内存对象,调用实例方法必然触发空指针异常。
补充说明:很多开发者会误以为Java传递对象类型参数是引用传递,实际传递的是「对象引用的拷贝值」。如果通过形参持有的地址修改对象内部属性,修改会同步到外部(因为两个变量指向同一个堆对象);但如果直接对形参本身重新赋值(设为null、指向新创建的对象),永远不会影响方法外的原始变量。
内容的提问来源于stack exchange,提问作者NicoHahn
相关产品推荐
相关产品推荐

