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:
- An explicit call to
super(args), invoking a specific superclass constructor. - An implicit call to
super()(the no-argument superclass constructor), inserted by the compiler if you do not write one yourself. - An explicit call to
this(args), which delegates to another constructor in the same class — this constructor will then eventually chain tosuper(...).
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:
- Superclass static initialisers and static fields (run once when the class is first loaded)
- Subclass static initialisers and static fields (run once when the class is first loaded)
- Superclass instance fields (initialised to their declared values or defaults)
- Superclass constructor body executes
- Subclass instance fields are initialised
- 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 fieldnameis initialised to"Unnamed"beforeAnimal’s constructor body runs.Dog’s instance fieldbreedis initialised to"Unknown"beforeDog’s constructor body runs.- Even though
breedis set to"Labrador"in the constructor, if we had tried to usebreedinAnimal’s constructor (via an overridden method), it would have beennullbecause theDogfields 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.