Skip to content
Part IA Michaelmas Term

What Gets Inherited and Constructor Chaining

What a Subclass Inherits

A subclass inherits:

  • All public members (methods and fields) from the superclass
  • All protected members from the superclass
  • Package-private (default access) members — if and only if the subclass is in the same package

A subclass does not inherit:

  • Private members — these exist in the superclass for the superclass’s own implementation and are invisible to the subclass. If a subclass needs access, the superclass must expose protected or public accessor methods.
  • Constructors — constructors are never inherited. A subclass must define its own constructors.

Constructor Chaining

Every subclass constructor must — directly or indirectly — invoke a superclass constructor. This ensures that the superclass’s state is properly initialised before the subclass adds its own state.

The first line of any constructor is either:

  1. An explicit call to super(args), invoking a specific superclass constructor.
  2. An implicit call to super() (the no-argument superclass constructor), inserted by the compiler if you do not write one yourself.
  3. An explicit call to this(args), which delegates to another constructor in the same class — this constructor will then eventually chain to super(...).
class Animal {
    private String name;

    public Animal(String name) {
        this.name = name;
    }
}

class Dog extends Animal {
    private String breed;

    public Dog(String name, String breed) {
        super(name);              // must be first — chaining to Animal(name)
        this.breed = breed;
    }
}

If the superclass has no no-argument constructor, the subclass must explicitly call some super(args) — otherwise the compiler tries to insert super() and fails:

class Animal {
    private String name;
    public Animal(String name) { this.name = name; }
    // No no-arg constructor!
}

class Dog extends Animal {
    public Dog() {
        // compile error: cannot find Animal()
    }
}

The Full Construction Order

When a subclass object is created with new, initialisation proceeds in this strict order:

  1. Superclass static initialisers and static fields (run once when the class is first loaded)
  2. Subclass static initialisers and static fields (run once when the class is first loaded)
  3. Superclass instance fields (initialised to their declared values or defaults)
  4. Superclass constructor body executes
  5. Subclass instance fields are initialised
  6. Subclass constructor body executes

Here is a concrete walkthrough:

class Animal {
    private String name = "Unnamed";
    private int id;

    static { System.out.println("Animal static init"); }

    public Animal() {
        System.out.println("Animal constructor: name=" + name);
    }
}

class Dog extends Animal {
    private String breed = "Unknown";

    static { System.out.println("Dog static init"); }

    public Dog(String breed) {
        System.out.println("Dog constructor start: breed=" + this.breed);
        this.breed = breed;
        System.out.println("Dog constructor end: breed=" + this.breed);
    }
}

public class Main {
    public static void main(String[] args) {
        Dog d = new Dog("Labrador");
    }
}

Output:

Animal static init
Dog static init
Animal constructor: name=Unnamed
Dog constructor start: breed=Unknown
Dog constructor end: breed=Labrador

Notice:

  • Static initialisers run first, superclass before subclass, only once when the class is loaded.
  • Animal’s instance field name is initialised to "Unnamed" before Animal’s constructor body runs.
  • Dog’s instance field breed is initialised to "Unknown" before Dog’s constructor body runs.
  • Even though breed is set to "Labrador" in the constructor, if we had tried to use breed in Animal’s constructor (via an overridden method), it would have been null because the Dog fields have not been initialised yet. This is a known trap: calling overridable methods from a constructor.

The Trap: Calling Overridable Methods from Constructors

Consider:

class Animal {
    public Animal() {
        speak();  // calls Dog.speak() if this is a Dog — but Dog fields not yet initialised
    }
    void speak() { System.out.println("..."); }
}

class Dog extends Animal {
    private String sound = "Woof";
    @Override
    void speak() { System.out.println(sound.toUpperCase()); }
}

public static void main(String[] args) {
    new Dog();  // NullPointerException — sound is null when speak() is called from Animal()
}

When new Dog() is invoked, Animal’s constructor runs first. It calls speak(), which — due to dynamic dispatch — resolves to Dog.speak(). But Dog’s fields have not been initialised yet, so sound is still null. Calling sound.toUpperCase() throws NullPointerException.

The rule: never call overridable methods from constructors. Either call only private or final methods, or document clearly that subclasses must not rely on their own fields in overridden methods called during construction.

super for Method Invocation

Within a subclass method, super.methodName() calls the superclass’s version of the method, bypassing the subclass’s override:

class Dog extends Animal {
    @Override
    void eat() {
        super.eat();  // call Animal's eat() first
        System.out.println("Dog finishes eating");
    }
}

This is commonly used to extend behaviour — do the superclass thing, then add subclass-specific logic. It cannot be used to skip multiple levels; super.super.eat() is not valid.