Constructors in Java Inheritance – Understanding Superclass and Subclass Constructor Behavior

Illustration for Constructors in Java Inheritance – Understanding Superclass and Subclass Constructor Behavior
By Last updated:

Introduction

Constructors are essential for object initialization in Java. But how do they behave when inheritance comes into play?

In Java, constructors are not inherited, but they play a vital role in initializing both superclass and subclass objects. Understanding how constructors work in an inheritance hierarchy is crucial to avoid runtime errors, bugs, and confusing code.

In this tutorial, you’ll learn how constructor chaining, super() calls, and default constructors work—along with real-world examples and gotchas.


What is a Constructor?

A constructor is a special method in Java used to initialize new objects. It shares the name of the class and has no return type.

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

Inheritance and Constructor Chaining

In Java, when a subclass object is created:

  1. The superclass constructor is always called first
  2. This happens via an implicit or explicit call to super()
  3. Constructors are not inherited, but are invoked in order

Java Constructor Chaining Example

class Animal {
    Animal() {
        System.out.println("Animal created");
    }
}

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

Output:

Animal created
Dog created

UML-style Representation

<<class>> Animal
+ Animal()

<<class>> Dog extends Animal
+ Dog()

Flow: Dog() ➝ Animal()


Using super() Explicitly

You can explicitly call a superclass constructor using super().

class Animal {
    Animal(String name) {
        System.out.println("Animal: " + name);
    }
}

class Dog extends Animal {
    Dog() {
        super("Bruno");
        System.out.println("Dog created");
    }
}

⚠️ Must be the first statement in the constructor.


Real-World Example

class User {
    String name;

    User(String name) {
        this.name = name;
    }
}

class AdminUser extends User {
    String role;

    AdminUser(String name, String role) {
        super(name); // initializes User
        this.role = role;
    }
}

Constructor Overloading with Inheritance

class Vehicle {
    Vehicle() {
        System.out.println("Default vehicle");
    }

    Vehicle(String type) {
        System.out.println("Vehicle type: " + type);
    }
}

class Car extends Vehicle {
    Car(String type) {
        super(type);
        System.out.println("Car created");
    }
}

Common Pitfalls

❌ No Default Constructor in Superclass

class Animal {
    Animal(String type) {}
}

class Dog extends Animal {
    Dog() {} // ERROR: No default constructor in Animal
}

✅ Fix:

Dog() {
    super("dog");
}

Constructors and final Fields

Java requires final fields to be initialized in constructors.

If a subclass inherits a class with final fields, they must be initialized via a valid constructor chain.


Java 17/21 Notes

Records and Constructors

Java records generate constructors automatically.

record Person(String name, int age) {}

You can still define custom constructors in records using this().

Sealed Class + Constructor

Sealed classes often have non-public constructors to restrict subclassing.

sealed class Vehicle permits Car {}

final class Car extends Vehicle {
    Car() {
        super(); // allowed
    }
}

Real-World Analogy

Think of object construction like assembling a layered sandwich.

  • First, the bread base (superclass constructor) is laid down
  • Then the fillings (subclass constructor) are added
  • You can't skip the bread—it’s always initialized first!

Best Practices

  • Always call super(...) with required arguments in subclasses
  • Avoid complex logic in constructors—use factory methods if needed
  • Initialize all superclass state properly
  • Use super() in sealed class hierarchies to enforce structure
  • Avoid using this() and super() in the same constructor

Conclusion

Constructor behavior in Java inheritance might seem simple, but it can introduce subtle bugs if misunderstood. Whether you're extending classes in enterprise apps or working with sealed/record types, mastering constructor chaining is key to writing robust object-oriented code.


Key Takeaways

  • Constructors are not inherited but must be invoked
  • Subclass constructors always call the superclass constructor first
  • Use super(...) explicitly when needed
  • Avoid missing constructor calls in absence of default constructor
  • Java 17+ introduces sealed and record-specific constructor behavior

FAQs

1. Can a subclass constructor skip the superclass constructor?
No. The superclass constructor is always called—either implicitly or explicitly.

2. What happens if I don’t call super()?
The compiler inserts super() automatically only if the superclass has a no-arg constructor.

3. Can I use both this() and super() in the same constructor?
No. One must be the first statement—you can’t use both.

4. Are constructors inherited in Java?
No. Subclasses do not inherit constructors.

5. Can a subclass overload a superclass constructor?
No. Overloading happens within a class; subclasses can define their own constructors.

6. Can abstract classes have constructors?
Yes. Abstract classes can have constructors to initialize common state.

7. Do records in Java 16+ allow custom constructors?
Yes. Records allow compact or canonical constructors.

8. Are constructors part of the method overriding rules?
No. Constructors cannot be overridden.

9. Can static blocks replace constructors in inheritance?
No. Static blocks run only once during class loading—not during object creation.

10. What if the superclass has multiple constructors?
You must explicitly choose the correct one using super(...) in the subclass.