Which run first? default values for instance variables or Super Constructors?
Asked Answered
W

1

7

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);
    }
}
Wheeze answered 24/10, 2014 at 17:4 Comment(6)
i guess because you have initialize variables when declare .Bettencourt
but as the SCJP6 specifies, they said that default values are assigned before super constractor completes even if initialization is part of their declarationWheeze
i saw that the super contractor runs before instance variables get their default values how did you assert that? (post your log)Bug
by debugging in eclipse, it prints "Super Constructor run" before the variables i and j get their default valuesWheeze
default values are... default values. I.e. thay are not 6 and 8 in your example, but null and 0. You superclass constructor should display the values of i and j to demonstrate that.Raffle
i know what are default values, and i am using the "watch expression" in eclipse to observe their behaviorWheeze
H
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).

Hildegaard answered 24/10, 2014 at 17:27 Comment(2)
thats perfetct, and i understand from your example that variables are assigned their default values before the super constractor finishes. but can you tell me why for my example i can't see the value of "i" and "j" when using the "watch expression" till the super constractors complete? is it only a problem within eclipse ?Wheeze
Probably, yeah -- it could be that eclipse is trying to protect you against exactly this kind of confusion!Hildegaard

© 2022 - 2024 — McMap. All rights reserved.