Java Constructor Execution Order
java constructor execution order: Understand how static blocks, instance initializers, and super() calls determine the order of constructor execution in Java.
In Java, the order in which constructors and initialization blocks run is fixed by the language specification, but many developers discover it only after debugging a null field or a surprising side effect. Understanding java constructor execution order is essential for writing predictable class hierarchies and avoiding subtle bugs.
The Order of Static Initialization
Static initialization happens once when the class is first loaded into the JVM. This includes static field initializers and static initializer blocks. The order is top-down within the class, and for a class hierarchy, the superclass static initialization runs before the subclass. This is true even if the subclass is loaded first. The JVM ensures that a class's static initializers run before any instance of that class is created, and before any static method or field is accessed.
For example:
class Parent { static { System.out.println("Parent static block"); } } class Child extends Parent { static { System.out.println("Child static block"); } }
When Child is first referenced, the output is:
Parent static block
Child static block
This order is guaranteed by the JVM's class initialization mechanism.
Instance Initialization: super() First
Instance initialization is more involved. Every constructor must begin with a call to either this(...) or super(...). If you omit it, the compiler inserts super() with no arguments. That means the superclass constructor runs before any of the subclass's instance fields are initialized. After the super() call returns, the subclass's instance field initializers and instance initializer blocks run in the order they appear in the source file. Finally, the constructor body executes.
So the full sequence for creating a subclass instance is:
- Superclass static initialization (once)
- Subclass static initialization (once)
- Superclass instance initialization (its own field initializers, instance blocks, and constructor body)
- Subclass instance initialization (its own field initializers, instance blocks, and constructor body)
The key point is that the superclass constructor completes before any subclass instance field is assigned.
A Concrete Example of Execution Order
Let's look at a complete example:
class Base { static { System.out.println("Base static"); } { System.out.println("Base instance block"); } Base() { System.out.println("Base constructor"); } } class Derived extends Base { static { System.out.println("Derived static"); } { System.out.println("Derived instance block"); } Derived() { System.out.println("Derived constructor"); } } public class Main { public static void main(String[] args) { new Derived(); } }
The output is:
Base static
Derived static
Base instance block
Base constructor
Derived instance block
Derived constructor
Notice that the base constructor runs before the derived instance block. If Base had a field initializer, it would run before the Base constructor body. Similarly, Derived field initializers run after Base constructor returns but before the Derived constructor body.
Why Field Initializers Run After the Superclass Constructor
Field initializers are not executed at the point where they appear in the source. Instead, the compiler moves them into the constructor, immediately after the super() call. This is why a superclass constructor cannot rely on subclass fields being initialized. When the superclass constructor runs, the subclass instance is still in a partially constructed state. The subclass fields have their default values (null, 0, false) until the subclass's own initialization steps run.
This ordering is a deliberate design choice. It ensures that a superclass constructor can safely use its own fields, but it also means that calling a method that is overridden in the subclass from a superclass constructor can expose uninitialized state.
The Danger of Calling Overridable Methods in a Constructor
Consider this scenario:
class Parent { Parent() { printInfo(); } void printInfo() { System.out.println("Parent info"); } } class Child extends Parent { private String name = "Child"; @Override void printInfo() { System.out.println("Child name: " + name); } }
When you create new Child(), the Parent constructor calls printInfo(), which is overridden to use name. But at that point, name has not been initialized yet—it still holds null. The output is Child name: null, not Child name: Child.
The root cause is the execution order: the superclass constructor runs before subclass field initializers. This is a well-known anti-pattern. If you must call a method from a constructor, make it final or private so it cannot be overridden. Alternatively, move the initialization logic to a separate method and call it after construction.
Keeping Constructor Execution Order Predictable
The fixed order of constructor execution is part of the Java language specification, but that doesn't mean you should rely on it for complex logic. Overusing static initializers or instance blocks can make code hard to follow. Prefer explicit initialization in constructors and avoid side effects in initializer blocks. If you need to perform setup that depends on subclass state, consider a factory method or a build() method that is called after the object is fully constructed.
For example, instead of calling an overridable method from the constructor, you can expose a static factory that constructs the object and then calls an initialization method:
class Parent { private String info; protected Parent() { // no overridable calls } protected void init() { // default implementation } static Parent create() { Parent p = new Parent(); p.init(); return p; } } class Child extends Parent { private String name = "Child"; @Override protected void init() { super.init(); System.out.println("Child name: " + name); } static Child create() { Child c = new Child(); c.init(); return c; } }
Now name is initialized before init() is called, because the object is fully constructed. This pattern avoids the ordering trap while keeping the logic explicit.
Also note that static initialization order can matter when classes reference each other. The JVM will initialize a class only once, but circular references between classes can lead to null static fields if one class is initialized before another. Keeping static initialization simple and free of cross-class dependencies reduces these risks.