Java继承实例化时,父类构造为何调用子类重写方法?
Java构造方法中调用重写方法的底层机制解析
代码示例
// This example program aims at inspecting and understanding the order in which the individual // initialization steps are taken when executing this program. public class ConstructorTest { public static void main(String[] args) { new Sub(); } } class Super { Super() { System.out.println("Super constructor"); m(); } void m() { System.out.println("Method m() of class Super"); } } class Sub extends Super { Person p = new Person(); Sub() { System.out.println("Sub constructor"); m(); } @Override void m() { System.out.println("Method m() of class Sub"); System.out.println(p.getName()); } } class Person { String name; String getName() { return "John Doe"; } }
运行输出
"Super constructor" "Method m() of class Sub" NullPointerException
核心问题解答
你已经明确NullPointerException是因为子类的Person p未初始化就被调用,而父类构造执行时会调用子类重写的m()方法,核心原因在于Java的动态方法分派机制,和父类构造先执行的顺序无关,关键逻辑如下:
对象的实际类型从创建时就确定
执行new Sub()时,JVM第一步会为Sub类型的对象分配内存,此时这个对象的运行时实际类型已经是Sub——哪怕父类构造还在运行,对象头里已经记录了它的类型信息。方法表(Method Table)在类加载阶段提前构建
Java中所有可被重写的方法(非private、非static、非final的方法),会在类加载时生成对应的方法表。子类的方法表会继承父类的方法表,并将自身重写的方法替换成自己的实现。
当父类构造里调用m()时,JVM不会直接执行父类的方法,而是根据当前对象的实际类型(Sub)去查找对应的方法表,找到已经被替换的Sub.m()并执行。这不是什么“预先扫描子类”或“递归检查实现”,而是类加载阶段就已经把方法表准备完毕,运行时直接根据对象类型匹配对应方法。
重要提醒
这是Java继承体系中的典型陷阱:绝对不要在父类构造方法中调用可被重写的方法。因为父类构造执行时,子类的实例变量还未完成初始化,子类重写的方法如果访问这些变量,必然会抛出NullPointerException或者访问到不正确的初始值。
内容的提问来源于stack exchange,提问作者MounkeySoup318
相关产品推荐
相关产品推荐

