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

Java方法重写技术问询:父类Glyph与子类RoundGlyph代码解析

Java方法重写机制解析——结合构造器调用重写方法的典型场景

这是个非常经典的Java方法重写+对象初始化流程的问题,刚好能帮你把重写机制和构造阶段的坑点串起来讲清楚。

首先先明确:方法重写(Override)是子类对父类中非private、非static、非final的方法进行重新实现,它是Java实现运行时多态的核心。重写必须遵循“两同两小一大”规则:

  • 两同:方法名、参数列表完全相同
  • 两小:子类方法的返回值类型≤父类(比如父类返回Number,子类可以返回Integer)
  • 一大:子类方法的访问权限≥父类(比如父类是protected,子类可以是public)

先看你给出的代码示例

父类Glyph:

public class Glyph {
    void draw() {
        System.out.println("Glyph.draw()");
    }
    Glyph() {
        System.out.println("Glyph() before draw()");
        draw();
        System.out.println("Glyph() after draw()");
    }
}

子类RoundGlyph:

class RoundGlyph extends Glyph {
    int radius = 1;
    RoundGlyph(int r) {
        super();
        radius = r;
        System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
    }
    @Override // 建议加上这个注解,编译器帮你检查是否符合重写规则
    void draw() {
        System.out.println("RoundGlyph.draw(), radius = " + radius);
    }
}

测试代码与执行结果

假设我们用这段代码测试:

public class Test {
    public static void main(String[] args) {
        new RoundGlyph(5);
    }
}

执行后会输出:

Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5

是不是有点意外?为什么draw()里的radius是0而不是1?咱们一步步拆解执行流程:

  1. 调用子类构造器:当new RoundGlyph(5)执行时,首先进入子类的构造器,第一行显式调用super()(如果没写,编译器会自动加),也就是父类Glyph的构造器。
  2. 父类构造器执行:
    • 先打印Glyph() before draw()
    • 然后调用draw()方法——这里是关键!因为当前正在创建的是RoundGlyph实例,JVM会根据实际对象类型调用子类重写后的draw()方法(这就是运行时多态的体现),而不是父类的draw()。
  3. 子类重写的draw()执行:此时子类的成员变量radius还没完成初始化!Java对象初始化的顺序是:父类构造器执行 → 子类成员变量赋默认值(比如int是0)→ 子类构造器代码执行。所以此时radius还是默认值0,因此打印出RoundGlyph.draw(), radius = 0。
  4. 父类构造器收尾:打印Glyph() after draw(),父类构造完成。
  5. 回到子类构造器:
    • 执行radius = r;,把radius赋值为传入的5
    • 打印RoundGlyph.RoundGlyph(), radius = 5

关键知识点总结

  • 运行时多态的本质:JVM在调用方法时,是根据对象的实际类型(而不是引用类型)来决定调用哪个版本的方法,这也是重写能生效的核心。
  • 构造器调用重写方法的风险:父类构造器执行时,子类的成员变量还处于默认值状态(子类的初始化逻辑还没跑),此时调用子类重写的方法,很可能访问到未初始化的变量,导致不符合预期的结果。
  • 避坑建议:尽量不要在构造器中调用可被重写的方法(即非private、非final的方法);如果必须调用,确保该方法不依赖子类的成员变量,或者把方法标记为private/final,避免被子类重写。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:40:16