Back to Blog
Java

Java Inheritance Constructor Order: Superclass First

java inheritance constructor order: Learn how Java inheritance constructor order works: superclass constructors run before subclass constructors, and how to control it...

JavaInheritanceConstructorsObject Initializationsuper keyword
Illustration of a Java class hierarchy showing a superclass constructor executing before a subclass constructor, with a call chain arrow.

In Java, the order in which constructors run during inheritance is a fixed rule: a superclass constructor always executes before the subclass constructor body. Understanding java inheritance constructor order is essential for predicting object initialization and avoiding subtle bugs. When you create an instance of a subclass, the Java compiler ensures that the entire superclass chain is initialized first, from the topmost class down to the subclass itself.

The Basic Rule: Superclass Constructor Runs First

Consider a simple hierarchy where a Dog class extends an Animal class. Both classes have no-argument constructors that print a message:

class Animal { Animal() { System.out.println("Animal constructor"); } } class Dog extends Animal { Dog() { System.out.println("Dog constructor"); } }

When you execute new Dog(), the output is:

Animal constructor
Dog constructor

The superclass constructor completes before the subclass constructor body begins. This is not a coincidence; it is enforced by the Java language specification. The compiler inserts a call to super() as the first statement of the subclass constructor if no other constructor call is present.

How the Compiler Inserts the super() Call

If you do not explicitly write super() or this() at the start of a constructor, the compiler automatically inserts super() with no arguments. This implicit call invokes the superclass's no-argument constructor. If the superclass does not have a no-argument constructor, you will get a compile-time error.

class Animal { Animal(String name) { System.out.println("Animal: " + name); } } class Dog extends Animal { Dog() { // Compile error: no super() call, and Animal has no no-arg constructor } }

The error message typically says something like constructor Animal in class Animal cannot be applied to given types. To fix it, you must explicitly call super(name) with an appropriate argument.

Passing Arguments to the Superclass Constructor

When the superclass has parameterized constructors, you need to provide the required arguments explicitly. The super(...) call must be the first statement in the subclass constructor.

class Dog extends Animal { Dog(String name) { super(name); // must be first System.out.println("Dog: " + name); } }

Now new Dog("Rex") prints:

Animal: Rex
Dog: Rex

This explicit call gives you control over which superclass constructor is used and what values are passed to it.

Constructor Chaining Across Multiple Levels

Inheritance can span multiple levels. The constructor chain always starts from the root of the class hierarchy, Object, and proceeds down to the instantiated class.

class A { A() { System.out.println("A"); } } class B extends A { B() { System.out.println("B"); } } class C extends B { C() { System.out.println("C"); } }

Creating new C() produces:

A
B
C

Each constructor calls its parent's constructor before executing its own body. This ensures that all inherited state is fully initialized before any subclass-specific logic runs.

Common Pitfalls and Runtime Errors

One frequent mistake is forgetting that the superclass must have a no-argument constructor if you rely on the implicit super() call. This leads to compile errors that are easy to resolve by adding an explicit super(...) call or by providing a no-arg constructor in the superclass.

Another subtle issue arises when a superclass constructor calls a method that is overridden in the subclass. Because the superclass constructor runs before the subclass fields are initialized, the overridden method may see default values (like null or 0) instead of the expected initialized values.

class Base { Base() { init(); } void init() { System.out.println("Base init"); } } class Derived extends Base { String value = "derived"; @Override void init() { System.out.println("Derived init: " + value); } }

When you create new Derived(), the output is Derived init: null because the value field has not been assigned yet. The superclass constructor runs before subclass instance variable initializers. This is a classic source of bugs; avoid calling overridable methods from constructors.

Why This Order Matters for Object Initialization

The constructor order is not arbitrary; it guarantees that the superclass's state is fully established before the subclass relies on it. For example, if the subclass constructor uses a method inherited from the superclass, that method may depend on fields initialized in the superclass constructor. By running superclass constructors first, Java ensures those fields are ready.

Instance variable initializers in the subclass run after the superclass constructor returns and before the rest of the subclass constructor body executes. So the full initialization sequence for a subclass instance is:

  1. Superclass constructor (including its field initializers)
  2. Subclass instance variable initializers
  3. Remaining subclass constructor body

This ordering is consistent across all levels of inheritance.

Using this() to Call Another Constructor in the Same Class

You can also use this(...) to delegate to another constructor in the same class. Like super(...), it must be the first statement. When you use this(...), the delegated constructor will eventually call super(...), so the superclass chain still runs before any subclass-specific code.

class Dog extends Animal { Dog() { this("Unknown"); // calls Dog(String) } Dog(String name) { super(name); System.out.println("Dog: " + name); } }

In this example, new Dog() triggers Dog(String) which calls super(name). The order remains: Animal constructor, then Dog constructor body.

Designing Constructor Hierarchies for Maintainability

When you design a class hierarchy, think about the constructor requirements of each class. If a superclass has only parameterized constructors, every subclass must explicitly call super(...). This can become verbose but also makes dependencies clear. Alternatively, provide a protected no-argument constructor that supplies sensible defaults, reducing the burden on subclasses.

Avoid overusing inheritance when composition would be simpler. Deep constructor chains make code harder to trace and test. If you need many initialization parameters, consider a builder pattern or factory method to keep constructors focused and the order of initialization predictable.

Understanding java inheritance constructor order helps you write constructors that are safe, predictable, and maintainable. Knowing when the superclass code runs and what state is available at each stage prevents the kind of subtle bugs that only appear in production under specific conditions.

java inheritance constructor order: Practical Usage and Code | RYUSLOG DEV