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

时间:2014-10-24 17:04:14

标签: java constructor instance-variables scjp

根据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);
    }
}

1 个答案:

答案 0 :(得分:5)

初始化序列在JLS 12.5

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

      

    ...

         

    如果没有足够的空间可用于为对象分配内存,则使用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(或者传递给构造函数的任何内容)。