最终字段的初始化顺序

JB *_*zet 13 java constructor final jls

考虑这两个类:

public abstract class Bar {
    protected Bar() {
        System.out.println(getValue());
    }

    protected abstract int getValue();
}

public class Foo extends Bar {
    private final int i = 20;

    public Foo() {
    }

    @Override
    protected int getValue() {
        return i;
    }

    public static void main(String[] args) {
        new Foo();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我执行Foo,则输出为20.

如果我使字段非final,或者我在Foo构造函数中初始化它,则输出为0.

我的问题是:在最终字段的情况下,初始化顺序是什么?JLS中描述了这种行为?

我希望找到最终的领域一些特殊的规则在这里,但除非我错过了什么,没有.

请注意,我知道我永远不应该从构造函数中调用可覆盖的方法.这不是问题的关键.

Jes*_*per 17

您的final int i成员变量是常量变量:4.12.4.final变量

原始类型或类型的变量String,即final,用一个编译时间常量表达式(§15.28)初始化,被称为恒定变量.

这会对事物初始化的顺序产生影响,如 12.4.2所述.详细的初始化程序.

  • “详细初始化过程”的链接有点误导,因为它描述了类的初始化,换句话说,它仅与“静态”字段有关。实际上,`final`实例字段的分配与其他字段一样,但要点是`getValue()`不会读取该字段,而只是返回常量值。 (2认同)

Vog*_*612 5

以“字节码”的方式带您了解它的外观。

您应该已经意识到,构造函数中的第一个实际指令必须是调用super(无论是否带参数)。

当父构造函数完成并且超级“对象”完全构造完毕时,该超级指令返回。因此,当您构建时,Foo会发生以下情况(按顺序):

// constant fields are initialized by this point
Object.construction // constructor call of Object, done by Bar
Bar.construction // aka: Foo.super()
callinterface getValue() // from Bar constructor
// this call is delegated to Foo, since that's the actual type responsible
// and i is returned to be printed
Foo.construction
Run Code Online (Sandbox Code Playgroud)

如果您要在构造函数中初始化它,那么这将在“现在”发生,在getValue()已经被调用之后。