哪个先运行? 实例变量或超级构造函数的默认值?

根据SCJP6(页577),我发现实例变量在超类构造函数完成之前被赋予了默认值,我在Debugg模式中尝试了一个例子,但我看到超级承包商在实例变量获取其默认值之前运行,可以任何一个解释那对我来说?

用例以防万一有人想尝试一下:

package courseExercise; class test { test() { System.out.println("Super Constructor run"); } } public class Init extends test { private Integer i = 6; private int j = 8; Init(int x) { super(); System.out.println("1-arg const"); } Init() { System.out.println("no-arg const"); } static { System.out.println("1st static init"); } public static int d = 10; { System.out.println("1st instance init"); } { System.out.println("2nd instance init"); } static { System.out.println("2nd static init"); } public static void main(String[] args) { new Init(); new Init(7); } } 

初始化序列在JLS 12.5中指定:

  1. 首先,为新对象分配内存
  2. 然后,对象中的所有实例变量(包括在此类中定义的变量及其所有超类)都初始化为其默认值
  3. 最后,调用构造函数。

规范的相关部分是:

如果没有足够的可用空间来为对象分配内存,那么使用OutOfMemoryError突然完成类实例的创建。 否则,新对象中的所有实例变量(包括在超类中声明的那些变量)都将初始化为其默认值(第4.12.5节)。

在作为结果返回对新创建的对象的引用之前, 处理指示的构造函数以使用以下过程初始化新对象:

这是你永远不应该从构造函数调用非final方法的原因之一:该方法可能被子类覆盖,在这种情况下,该方法将在子类有机会设置该方法可能的状态之前调用需要。 考虑这个例子:

 public class Super { private final int superValue; protected Super() { superValue = getSuperValue(); } protected int getSuperValue() { return 1; } @Override public String toString() { return Integer.toString(superValue); } } public class Sub extends Super { private final int superValueOverride; public Sub(int value) { this.superValueOverride = value; } @Override protected int getSuperValue() { return superValueOverride; } public static void main(String[] args) { Super s = new Sub(2); System.out.println(s); } } 

看起来s.superValue应该是2,对吗? 毕竟, Sub覆盖getSuperValue()以返回superValueOverride的值,该值初始化为2.但是在初始化Sub的任何字段之前调用该方法(除了它们的默认值之外),因此s.superValue实际上是0( superValueOverride的默认值)。

这甚至更奇怪,因为superValueOverridefinal ,但它似乎改变了它的价值! 当Super调用getSuperValue() ,它为0,并且只有在Super构造函数完成之后,它才会被赋予其最终值2(或者传递给构造函数的任何内容)。