Tricky Output: Initialization Order

Difficulty: Advanced

Question

Predict the output when a parent constructor calls an overridable method, and both classes have static blocks, instance blocks and field initialisers.

Answer

This is the boss level of Java output questions, and it is popular with Amazon and Adobe interviewers because it tests class loading, object creation, and polymorphism at the same time. Learn the order once and you can solve any variant.

The order for creating an object of a subclass is as follows. First, when the class is first used, the JVM initialises the parent class, then the child class, running static field initialisers and static blocks in textual order, once per class. Then, for each new object, memory is allocated and all instance fields are set to default values (zero, false, null). Next the constructor of the child begins, but its very first action is to call the parent constructor. In the parent, instance field initialisers and instance blocks run in textual order, then the parent constructor body. When control returns to the child, its instance field initialisers and blocks run in textual order, and then the rest of the child constructor body executes.

Now the dangerous part. If the parent constructor calls a method that the child overrides, dynamic dispatch invokes the child's version even though the child's own fields have not been initialised yet. They still hold their default values. In the example, Base constructor prints Base ctor and then calls init(). Because the actual object is Derived, Derived.init runs and prints x = 0, not 5. Only afterwards does Derived's field initialiser assign x = 5, and the Derived instance block and constructor then see 5.

Following the rules on the example, the output is: Base static, Derived static (the whole class-loading phase comes first), then Base instance, Base ctor, Derived init x=0, Derived instance, Derived ctor x=5. Notice that the static blocks print even before the constructor starts, and they print only once, even if we create ten objects.

Why is calling overridable methods from constructors dangerous? Because the overriding method might depend on state the child constructor has not yet set up, leading to NullPointerException or wrong values that are extremely hard to debug. Effective Java has an item on this: constructors must not invoke overridable methods. The safe alternatives are to call only private, static, or final methods from constructors, or to use a factory method or lazy initialisation.

Similar traps involve final fields (a final field read in an overridden method called from the parent constructor also shows its default value), static fields initialised in textual order (a static reference used before its assignment reads null or zero), and exceptions thrown in static blocks, which cause ExceptionInInitializerError and make the class unusable, resulting in NoClassDefFoundError on later access.

In C++ the behaviour is different and worth contrasting: while a base constructor runs, the object's dynamic type is Base, so virtual calls do not reach the derived override. Java takes the opposite approach, which makes it more surprising.

Code examples

Initialisation order with a virtual call from the parent constructor

class Base {
    static { System.out.println("Base static"); }
    { System.out.println("Base instance"); }

    Base() {
        System.out.println("Base ctor");
        init();
    }
    void init() { System.out.println("Base init"); }
}

class Derived extends Base {
    static { System.out.println("Derived static"); }
    int x = 5;
    { System.out.println("Derived instance"); }

    Derived() { System.out.println("Derived ctor x=" + x); }
    @Override void init() { System.out.println("Derived init x=" + x); }
}

public class InitOrder {
    public static void main(String[] args) {
        new Derived();
    }
}

x is still 0 when the parent constructor triggers Derived.init, because Derived field initialisers run after super() returns.

Key points

Concepts covered

initialization-order, constructors, static-blocks, virtual-call-in-constructor