78

Can anyone explain the output of following program? I thought constructors are initialized before instance variables. So I was expecting the output to be "XZYY".

class X {
    Y b = new Y();

    X() {
        System.out.print("X");
    }
}

class Y {
    Y() {
        System.out.print("Y");
    }
}

public class Z extends X {
    Y y = new Y();

    Z() {
        System.out.print("Z");
    }

    public static void main(String[] args) {
        new Z();
    }
}
Joachim Sauer
  • 291,719
  • 55
  • 540
  • 600
Praveen Kumar
  • 1,534
  • 5
  • 16
  • 21
  • 5
    **TL;DR:** The new's in the class member initializations are implicitly put at the top of the constructors. – Andrew May 08 '18 at 15:24

4 Answers4

125

The correct order of initialisation is:

  1. Static variable initialisers and static initialisation blocks, in textual order, if the class hasn't been previously initialised.
  2. The super() call in the constructor, whether explicit or implicit.
  3. Instance variable initialisers and instance initialisation blocks, in textual order.
  4. Remaining body of constructor after super().

See sections §2.17.5-6 of the Java Virtual Machine Specification.

user207421
  • 298,294
  • 41
  • 291
  • 462
  • 3
    Link to JLS for Java 8: http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.5 – azurefrog Jan 13 '16 at 21:44
  • And also wrong - see my comment to @ÓscarLópez with same issue. Note that `static` is simply not part of the documentation section you are referring. – YoYo Feb 18 '18 at 03:20
  • The other article with the comment has been deleted - but I supplied the specifics in a supplementary answer with sample code. Not a complete answer - but at least highlights the `static {}` case. – YoYo Feb 18 '18 at 03:38
  • @YoYo This answer agrees with the JLS: specifically, in respect of the execution you mention in your answer, with [§12.4.2](http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4.2) item 9. If your implementation doesn't comply with the JLS, report it as a bug. – user207421 Mar 02 '18 at 06:08
  • What confuses is that class and instance instantiation is discussed separately. I guess that leaves implementation specifics open in how the two will intermix. – YoYo Mar 02 '18 at 06:27
  • It's worth pointing out that the "in textural order" in step 3 applys to interleaved order of both instance initialisation blocks(IIB) and Instance variable initialiser(IVB) which means there can be the execution order such as IIB1, IVB1, IIB2, IVB2. – Murphy Ng Feb 09 '19 at 13:30
  • @YoYo Not at all. The class has to be fully initialized before the instance can be created. Surely this is obvious? – user207421 Jun 25 '19 at 10:57
  • See my answer - I was able to create an instance *before* full static initialization. All I had to do is create the instance in a static context, kind of the same way enum values are created. – YoYo Jul 01 '19 at 01:05
71

If you look at the decompiled version of the class file

class X {
    Y b;

    X() {
        b = new Y();
        System.out.print("X");
    }
}

class Y {
    Y() {
        System.out.print("Y");
    }
}

public class Z extends X {

    Y y;

    Z() {
        y = new Y();
        System.out.print("Z");
    }

    public static void main(String args[]) {
        new Z();
    }
}

You can find that the instance variable y is moved inside the constructor, so the execution sequence is as follows

  1. Call the constructor of Z
  2. It triggers the default constructor of X
  3. First line of X constructor new Y() is called.
  4. Print Y
  5. Print X
  6. Call the first line in constructor Z new Y()
  7. Print Y
  8. Print Z

All the instance variables are initialized by using constructor statements.

Arun P Johny
  • 376,738
  • 64
  • 519
  • 520
4

When you invoke a constructor, the instance variable initializers run before the body of the constructor. What do you think the output of the below program ?

public class Tester {
    private Tester internalInstance = new Tester();
    public Tester() throws Exception {
        throw new Exception("Boom");
    }
    public static void main(String[] args) {
        try {
            Tester b = new Tester();
            System.out.println("Eye-Opener!");
        } catch (Exception ex) {
            System.out.println("Exception catched");
        }
    }
}

The main method invokes the Tester constructor, which throws an exception. You might expect the catch clause to catch this exception and print Exception catched. But if you tried running it, you found that it does nothing of that sort and It throws a StackOverflowError.

Vivek Pratap Singh
  • 8,296
  • 5
  • 17
  • 34
  • 2
    So you are basically trying to show that the constructor will keep on invoking itself (because of the line private Tester internalInstance = new Tester();) before the control comes to the throw section. Is my understanding correct? – Sen Dec 25 '18 at 14:42
2

To clarify the misconceptions with static - I will simply refer to this small piece of code:

public class Foo {

    {
        System.out.println("Instance Block 1");
    }

    static {
        System.out.println("Static Block 1");
    }

    public static final Foo FOO = new Foo();

    {
        System.out.println("Instance Block 2");
    }

    static {
        System.out.println("Static Block 2 (Weird!!)");
    }

    public Foo() {
        System.out.println("Constructor");
    }

    public static void main(String[] args) {
        System.out.println("In Main");
        new Foo();
    }
}

Surprise is that the output is as follows:

Static Block 1
Instance Block 1
Instance Block 2
Constructor
Static Block 2 (Weird!!)
In Main
Instance Block 1
Instance Block 2
Constructor

Note that we have a static {} that is called after two instance {}. this happens because we throw in the constructor in the middle, interjecting execution order the first time the constructor is called.

Discovered this when I was working on this answer - https://stackoverflow.com/a/30837385/744133.

Basically we observe this to happen:

  1. During the first time an object is initialized, Initialize current object for both static and instance initialization intermixed based on order of occurrence

  2. For all next initializations, only do the instance initialization in the order of occurrence, as static initialization already happened.

I need to research how the mix of inheritance, and both explicit and implicit calls to super and this will affect this, and will update with findings. It would be likely similar to the other supplied answers, except that they got it wrong with the static initialization.

MC Emperor
  • 20,870
  • 14
  • 76
  • 119
YoYo
  • 8,651
  • 8
  • 56
  • 70
  • 1
    This is because of `private static Foo instance = new Foo();`, which runs before the 2nd static block. In fact it *is* the 2nd static block, and what you have labelled 2nd is really 3rd. No mystery here. – user207421 Mar 15 '21 at 05:33
  • 1
    I think the takeaway here is to always make sure that `static` fields containing instantiations of the class they're written in, are the last `static` initializations within the class. For example, wrong is: `class Color { static final Color RED = new Color(255,0,0); static { ... } }`, right is: `class Color { static { ... } static final Color RED = new Color(255,0,0); }`. – MC Emperor May 12 '22 at 11:45