final 字段的初始化顺序

Initialization order of final fields

考虑这两个 类:

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();
    }
}

如果我执行 Foo,输出是 20。

如果我将字段设置为非最终字段,或者如果我在 Foo 构造函数中对其进行初始化,则输出为 0。

我的问题是:final 字段的初始化顺序是什么?JLS 中在哪里描述了这种行为?

我希望找到一些关于 final 字段的特殊规则 here,但除非我遗漏了什么,否则不会。

请注意,我知道我永远不应该从构造函数中调用可重写的方法。这不是问题的重点。

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

A variable of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28), is called a constant variable.

这会影响事物的初始化顺序,如 12.4.2. Detailed Initialization Procedure 中所述。

以 "byte-code-ish" 的方式向您介绍它的外观。

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

当父构造函数完成并且超级"object"完全构造时,超级指令returns。因此,当您构造 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

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