Skip to content
Ameen Abozaid
PublishedBEG-022-L02

Constructors and super

  • Lesson 2 of 7
  • 10 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

In the last lesson you used super(owner, balance) once, on trust. This lesson explains it. A SavingsAccount object is not built from nothing: it contains the whole Account part (the owner and the balance) plus its own part (the rate). Somebody has to build the parent part, and the language makes sure it happens first.

We continue with the family tree: a child cannot exist without its parent, so the parent is always "born" first. The tool for talking to the parent is the keyword super.

Why this matters: forgetting super(...) is the most common compile error when beginners start with inheritance. Knowing the rule turns a confusing message into a one-line fix.

This lesson builds on "Reusing behavior with extends" and on constructors and this(...) from the previous module.

Learning Objectives

By the end of this lesson you will be able to:

  • Explain why constructors are not inherited.
  • Write a subclass constructor that calls a superclass constructor with super(...).
  • State where super(...) must appear and what Java inserts when you write nothing.
  • Predict the order in which the parts of an object are built.
  • Read and fix the compiler error caused by a missing super(...) call.
  • Call the parent's version of a method with super.method().

Written Explanation

Constructors belong to one class

A constructor's job is to put a new object into a valid first state. Account(String owner, double balance) knows how to do that for an Account. A SavingsAccount has an extra field, rate, which the Account constructor knows nothing about. So constructors are not inherited: each class writes its own.

But the parent part still has to be set up properly, with the parent's rules. The subclass constructor therefore asks the parent's constructor to do it.

super(...): calling the parent constructor

Inside a subclass constructor, super(arguments) calls a constructor of the direct superclass:

java
SavingsAccount(String owner, double balance, double rate) {
    super(owner, balance);   // build the Account part
    this.rate = rate;        // then build the SavingsAccount part
}

The arguments decide which parent constructor runs, exactly as with new: super(owner, balance) matches Account(String, double).

The Java tutorial states the key rule: a call to a superclass constructor must be the first line of the subclass constructor. Putting anything before it is a compile-time error. This guarantees the parent part exists before the child part uses it.

The order of construction

When you create a Puppy that extends Dog that extends Animal, the constructors run from the top of the chain down. The tutorial describes this as constructor chaining that goes up the hierarchy until it reaches Object's constructor; what that means for you is: parent first, child last.

java
class Animal {
    Animal() { System.out.println("1. Animal part built"); }
}

class Dog extends Animal {
    Dog() { System.out.println("2. Dog part built"); }
}

class Puppy extends Dog {
    Puppy() { System.out.println("3. Puppy part built"); }
}

new Puppy() prints:

text
1. Animal part built
2. Dog part built
3. Puppy part built

Notice that Dog() and Puppy() never wrote super(). Java did it for them.

What Java inserts when you write nothing

If a constructor does not start with super(...) (or this(...)), the compiler inserts a call to the parent's no-argument constructor, super(). Two cases follow:

  1. The parent has a no-argument constructor (a class with no constructors at all gets a default one): everything works silently, as in the Puppy example.
  2. The parent has constructors but none without parameters: the implicit super() has nothing to call, and the compiler reports an error.

Here is case 2. Account only has Account(String, double), and the subclass constructor forgets super(...):

java
class SavingsAccount extends Account {
    private double rate;

    SavingsAccount(String owner, double rate) {
        this.rate = rate;
    }
}

Compiling with JDK 21 gives:

text
Main.java:14: error: constructor Account in class Account cannot be applied to given types;
    SavingsAccount(String owner, double rate) {
                                              ^
  required: String,double
  found:    no arguments
  reason: actual and formal argument lists differ in length

Read it as: "you are building an Account, and you did not tell it the String and double it requires." The fix is to add super(owner, 0); (or take a balance parameter) as the first line.

The same message appears when a class has no constructor at all but its parent has no no-argument constructor, because the compiler-provided default constructor also calls super().

Constructors can still chain with this(...)

From the previous module you know this(...) calls another constructor of the same class. It also must be the first statement, so a constructor uses either this(...) or super(...), not both. The chain ends at a constructor that calls super(...):

java
SavingsAccount(String owner, double balance, double rate) {
    super(owner, balance);
    this.rate = rate;
}

SavingsAccount(String owner, double rate) {
    this(owner, 0, rate);     // reuse the constructor above
}

super.method(): the parent's version

super followed by a dot and a method name calls the parent's version of a method. You use it when the child wants to add to the parent's behavior instead of replacing it. Suppose Account has describe() and SavingsAccount declares its own describe() with the same signature:

java
String describe() {
    return super.describe() + " (rate " + rate + ")";
}

This is called overriding, the subject of the next lesson. Here, just see the tool: super.describe() runs the code written in Account, then the child appends its own text.

A fine point to keep: super(...) with parentheses is a constructor call that can appear only as the first statement of a constructor. super.name(...) with a dot is a method call that can appear anywhere in an instance method.

A note about Java versions

The "first statement" rule is the rule in the Java versions this course uses. Newer Java releases have been relaxing what may appear before super(...). Do not rely on that in beginner code: keep super(...) as the first line.

Examples

Example 1: Animal and Dog with a name (simple)

java
class Animal {
    private final String name;

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

    String getName() { return name; }
}

class Dog extends Animal {
    Dog(String name) {
        super(name);
    }
}

public class Main {
    public static void main(String[] args) {
        Dog d = new Dog("Rex");
        System.out.println(d.getName());
    }
}

Output:

text
Rex

Dog has no fields of its own, but its constructor still has to pass the name to Animal. name stays private in Animal: Dog never sees the field, only the parent's code does.

Example 2: construction order (practical)

The Animal, Dog, Puppy classes above prove the order: output lines 1, 2, 3, parent before child. This matters in practice because a child constructor can safely use anything the parent set up, since it is already finished. It also means you should not call a method from a parent constructor and expect child data to be ready, a trap Lesson 4 returns to.

Example 3: SavingsAccount with two constructors (realistic)

java
class Account {
    private final String owner;
    private double balance;

    Account(String owner, double balance) {
        this.owner = owner;
        this.balance = balance;
    }

    String describe() {
        return owner + ": " + balance;
    }
}

class SavingsAccount extends Account {
    private final double rate;

    SavingsAccount(String owner, double balance, double rate) {
        super(owner, balance);
        this.rate = rate;
    }

    SavingsAccount(String owner, double rate) {
        this(owner, 0, rate);
    }

    String describe() {
        return super.describe() + " (rate " + rate + ")";
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(new SavingsAccount("Sara", 500, 0.05).describe());
        System.out.println(new SavingsAccount("Omar", 0.03).describe());
    }
}

Output:

text
Sara: 500.0 (rate 0.05)
Omar: 0.0 (rate 0.03)

The two-argument constructor starts accounts at zero by delegating with this(...), which in turn calls super(...). In the next lesson you will add @Override to describe(); here it works without, which is exactly the habit to fix.

Backend Insight

Real backend classes validate in their constructors: an Account may refuse a negative starting balance. Because a subclass constructor must call a parent constructor first, that validation always runs, and the subclass cannot create a half-built parent part. This is how a base class protects its invariants even from its own children.

You will also meet this in frameworks: a class you write may have to call super(...) with configuration the framework base class needs. The compiler error above is what you will see if you forget it. You can now read it in seconds.

Common Mistakes

Mistake 1: Forgetting super(...) when the parent has no no-argument constructor

You saw the error above. Add super(...) as the first line with arguments the parent's constructor accepts.

Mistake 2: Putting statements before super(...)

java
Mid() {
    System.out.println("hi");
    super(1);
}
text
error: call to super must be first statement in constructor

Move super(...) to the first line. Do any preparation inside the arguments, for example super(name.trim()).

Mistake 3: Using both this(...) and super(...)

Each must be first, so one constructor can have only one. Let this(...) delegate to a constructor that calls super(...).

Mistake 4: Assigning the parent's private field in the child

this.owner = owner; in SavingsAccount fails because owner is private in Account. Pass the value to super(...) instead.

Mistake 5: Confusing super(...) with super.method()

Parentheses directly after super call a constructor, dot-name calls a method.

Exercises

Try each exercise yourself before opening the solutions.

Level A: Foundation

A1. What does the compiler insert at the start of a subclass constructor that does not begin with super(...) or this(...)?

Show solution A1

A1. A call to the parent's no-argument constructor, super();.

A2. class Parent { Parent(int x) { } } and class Child extends Parent { Child() { } }. Does Child compile? Why or why not?

Show solution A2

A2. No. Child() starts with an implicit super(), but Parent only has Parent(int). The compiler reports that the constructor cannot be applied to the given types.

A3. With class A { A() { print("A"); } } and class B extends A { B() { print("B"); } } (where print stands for System.out.println), what does new B() print, in order?

Show solution A3

A3. A then B: the parent part first, then the child.

Level B: Application

B1. Write Book with a private final title set by its constructor and a getTitle(). Write EBook extends Book with a private final sizeMb and an info() method returning title (sizeMb MB). Add a second EBook constructor taking only the title that defaults the size to 1.

Show solution B1

B1.

java
class Book {
    private final String title;
    Book(String title) { this.title = title; }
    String getTitle() { return title; }
}

class EBook extends Book {
    private final int sizeMb;

    EBook(String title, int sizeMb) {
        super(title);
        this.sizeMb = sizeMb;
    }

    EBook(String title) {
        this(title, 1);
    }

    String info() { return getTitle() + " (" + sizeMb + " MB)"; }
}

new EBook("Java").info() gives Java (1 MB) and new EBook("Java", 5).info() gives Java (5 MB).

B2. Fix this class. Do not change Vehicle.

java
class Vehicle { Vehicle(String plate) { } }
class Car extends Vehicle {
    Car() { }
}
Show solution B2

B2. Give Car a plate to pass up, for example Car(String plate) { super(plate); }, or call super("unknown") in the existing no-argument constructor.

B3. With A and B extended to have A(String s) and B(String s) { super(s); ... }, explain what new B("x") does first.

Show solution B3

B3. It first runs A(String) with "x", because B's constructor starts with super(s). Then the rest of B's constructor body runs. Output: A with x then B with x.

Level C: Challenge

C1. Three classes Animal(String name), Dog(String name), Puppy(String name) each print their class name in the constructor after calling super(name). What is printed for new Puppy("Bo")? Where would you put a print so that "starting" appears before everything?

Show solution C1

C1. Parent first, child last: Animal, Dog, Puppy, each printing its own name. A "starting" message cannot go inside the constructors because super(...) must come first. Print it in main before new Puppy("Bo"), or inside an argument expression passed to super.

C2. A colleague says: "super(...) just copies the parent's fields into the child." Correct this in two sentences.

Show solution C2

C2. super(...) does not copy fields; it runs a constructor of the parent class that sets up the parent part of the same single object. There is one object, built in layers, not two objects.

Mini Challenge

Write Person with a private name and a constructor that rejects an empty name by printing Invalid name and storing "unknown" instead. Write Student extends Person with a school field. Give Student a constructor (name, school) using super(name). Create new Student("", "Cairo University") and print the stored name. Then delete the super(name) line, compile, and read the error.

Check your work: the validation inside Person ran even though you constructed a Student, and the compiler explained the missing arguments.

Summary & Next Lesson

Key takeaways

  • Constructors are not inherited; every class writes its own.
  • A subclass constructor starts with super(...) (or this(...)). If you write nothing, Java inserts super().
  • If the parent has no no-argument constructor, you must write super(...) yourself.
  • Objects are built parent first, child last.
  • super.method() calls the parent's version of a method; it is different from super(...).

Next lesson

The next lesson, "Overriding methods", shows how a subclass replaces a parent's behavior, and why @Override is worth typing every time.

Self-check

  1. Why are constructors not inherited?
  2. What must be the first statement of a subclass constructor that has no this(...)?
  3. When does the implicit super() cause a compile error?
  4. In what order are the constructors of a three-level chain run?
  5. What does super.describe() do?
Show answers
  1. A constructor builds one specific class, so each class writes its own.
  2. A call super(...). If you omit it, Java inserts super().
  3. When the parent has no accessible no-argument constructor.
  4. From the top parent down to the child.
  5. It runs the parent's version of describe().

References

  • Oracle, Java Tutorials, Using the Keyword super. Supports: super(...) must be the first line of the constructor, the compiler inserts a no-argument call when you omit it, the error when none exists, and constructor chaining up to Object.
  • Oracle, Java Language Specification SE 21, Chapter 8, Classes. Consulted for the single-superclass rule; I did not read the full constructor section, so the note about newer Java versions relaxing the first-statement rule is from general knowledge and is hedged in the text.
  • The error messages and outputs above were produced by compiling and running the code with JDK 21.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.