构造器内部多态方法的行为【摘自 On Java8】

248 阅读1分钟

构造器调用的层次结构带来了一个困境。如果在构造器中调用了正在构造的对象的动态绑定方法,会发生什么呢?

翻译过来:class B 继承 class A, B 重写了A 中的 f(), A 在构造器中调用了 f(),现在如果初始化B,会出现什么情况呢?

// polymorphism/PolyConstructors.java
// Constructors and polymorphism
// don't produce what you might expect
class Glyph {
    void draw() {
        System.out.println("Glyph.draw()");
    }

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

class RoundGlyph extends Glyph {
    private int radius = 1;

    RoundGlyph(int r) {
        radius = r;
        System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
    }

    @Override
    void draw() {
        System.out.println("RoundGlyph.draw(), radius = " + radius);
    }
}

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

错误输出:

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

我们需要明确一点,Java 中属性的初始化,其实是分为两步的:

  1. 为属性开辟内存空间,赋予初始值(0,false,null)
  2. 将属性初始化为用户指定的默认值

正确输出:

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

解析:

  1. main() 方法执行,准备初始化一个 RoundGlyph 对象

  2. 初始化 RoundGlyph 时,发现有父类 Glyph 需要先初始化

  3. 初始化 Glyph,默认调用无参构造器 Glyph()

    1. 输出 Glyph() before draw()
    2. 执行 draw()
      1. 因为是在初始化 RoundGlyph,所以需要调用 RoundGlyph.draw() 【或许,有大佬提供更合理的解释?】
      2. 执行 draw() 的过程中,发现需要使用变量 radius,此时,radius 仅仅被分配存储空间,被赋予默认初值,还未被用户指定的初值 1 覆盖
      3. 输出结果为 "RoundGlyph.draw(), radius = 0
    3. 输出 Glyph() after draw()
  4. Glyph初始化完成,开始初始化 RoundGlyph

  5. radius 的值,初始化为 1

  6. 执行构造器 RoundGlyph(int r)

    1. 覆盖 radius 的值,为 5
    2. 输出 RoundGlyph.draw(), radius = 5