You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的动态方法分派机制,和父类构造先执行的顺序无关,关键逻辑如下:

  1. 对象的实际类型从创建时就确定
    执行new Sub()时,JVM第一步会为Sub类型的对象分配内存,此时这个对象的运行时实际类型已经是Sub——哪怕父类构造还在运行,对象头里已经记录了它的类型信息。

  2. 方法表(Method Table)在类加载阶段提前构建
    Java中所有可被重写的方法(非private、非static、非final的方法),会在类加载时生成对应的方法表。子类的方法表会继承父类的方法表,并将自身重写的方法替换成自己的实现。

当父类构造里调用m()时,JVM不会直接执行父类的方法,而是根据当前对象的实际类型(Sub)去查找对应的方法表,找到已经被替换的Sub.m()并执行。这不是什么“预先扫描子类”或“递归检查实现”,而是类加载阶段就已经把方法表准备完毕,运行时直接根据对象类型匹配对应方法。

重要提醒

这是Java继承体系中的典型陷阱:绝对不要在父类构造方法中调用可被重写的方法。因为父类构造执行时,子类的实例变量还未完成初始化,子类重写的方法如果访问这些变量,必然会抛出NullPointerException或者访问到不正确的初始值。

内容的提问来源于stack exchange,提问作者MounkeySoup318

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 04:42:50