Java中子类对象创建时机及构造期方法重写引发NPE的疑问
这是个非常经典的Java构造器陷阱问题,咱们一步步把它拆解明白:
一、SubClass对象到底什么时候创建?
当你执行new SubClass()的瞬间,JVM就已经为SubClass对象分配了内存空间——这时候这个对象已经存在了,只不过它的所有成员变量(包括继承自父类的)都会被先设置成默认值:比如引用类型默认是null,基本类型默认是0、false这类。构造器的作用只是把对象从“默认初始状态”改成“你想要的状态”,而不是等构造完才创建对象。
二、为什么父类构造器里调用foo()会触发子类的重写方法?
Java里的非私有方法调用是运行时动态绑定的:简单说,调用哪个版本的方法,看的是实际创建的对象类型,而不是代码里引用的声明类型。
在父类构造器执行时,虽然当前代码在SuperClass的构造逻辑里,但实际存在的对象是SubClass实例(因为你new的是SubClass)。所以当父类构造器调用foo()时,JVM会自动找到SubClass中重写的foo()方法去执行,而不是父类自己的版本——这就是方法重写在构造阶段依然生效的原因。
三、空指针异常到底怎么来的?
咱们把整个构造流程走一遍就清楚了:
- 执行
new SubClass(),JVM分配SubClass对象内存,成员变量str被默认设为null - 子类构造器里会隐式调用父类的无参构造
super()(如果你没手动写,Java会自动加) - 父类构造器执行
foo(),因为动态绑定,实际调用的是SubClass的foo() - SubClass的
foo()里尝试调用str.toLowerCase(),但此时str还没被赋值(子类构造器里的str = "SubClass"要等父类构造完全执行完才会跑),所以str还是null,直接触发空指针异常 - 异常抛出后,子类构造器里的赋值代码根本没机会执行
四、如何避免这类坑?
- 绝对不要在构造器里调用可被重写的方法(也就是非
private、非final、非static的方法) - 如果父类构造需要执行初始化逻辑,把逻辑放到
private或者final的方法里,这样子类无法重写,就不会出现动态绑定到子类方法的问题
内容的提问来源于stack exchange,提问作者Abhilash
相关产品推荐
相关产品推荐

