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"。
考虑这两个 类:
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 isfinal
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"。