哪个先运行?实例变量或超级构造函数的默认值? [英] Which run first? default values for instance variables or Super Constructors?

查看:167
本文介绍了哪个先运行?实例变量或超级构造函数的默认值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据SCJP6(第507页),我发现实例变量在超类构造函数完成之前被赋予默认值,我在Debugg模式中尝试了一个示例,但是我看到超级承包商在实例变量获得其默认值之前运行,可以


$ b

$ b

  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 < a>:


  1. 首先,为新对象分配内存


  2. 最后,调用构造函数。

  3. ol>

    规范的相关部分是:


    ...



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

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



    ...


    这是为什么你不应该调用一个非 - 最终方法:该方法可能被子类覆盖,在这种情况下,方法将在子类有机会设置该方法可能需要的状态之前被调用。考虑这个例子:

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

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



    这甚至更糟糕,因为 superValueOverride final ,但它似乎改变其值!当 Super 调用 getSuperValue()时,只有在 Super 构造函数finishes是它的最终值为2(或任何传递给构造函数)。


    According to the SCJP6 (Page 507) i found that instance variables are assigned default values before the superclass constructors complete, i tried an example in Debugg mode but i saw that the super contractor runs before instance variables get their default values, could any one explain that to me ?

    Example i used in case someone want to try it:

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

    解决方案

    The initialization sequence is specified in JLS 12.5:

    1. First, memory is allocated for the new object
    2. Then all instance variables in the object (including the ones defined in this class and all of its superclasses) are initialized to their default values
    3. Finally, the constructor is called.

    The relevant part of the spec is:

    ...

    If there is not sufficient space available to allocate memory for the object, then creation of the class instance completes abruptly with an OutOfMemoryError. Otherwise, all the instance variables in the new object, including those declared in superclasses, are initialized to their default values (§4.12.5).

    Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:

    ...

    This is one of the reasons that you should never invoke a non-final method from a constructor: that method might be overridden by a subclass, in which case the method will be invoked before the subclass has the chance to set state that the method may need. Consider this example:

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

    It looks like s.superValue should be 2, right? After all, Sub overrides getSuperValue() to return the value of superValueOverride, which is initialized to 2. But that method gets invoked before any of Sub's fields are initialized (other than to their default values), so s.superValue is actually 0 (the default value of superValueOverride).

    This is even weirder because superValueOverride is final, and yet it seems to change its value! It's 0 when Super calls getSuperValue(), and only after the Super constructor finishes is it assigned its final value of 2 (or whatever is passed in to the constructor).

    这篇关于哪个先运行?实例变量或超级构造函数的默认值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆