Command Palette

Search for a command to run...

PHASE 5Intermediate ~29 min· topic 2 of 13

Topic 5.2

The super Keyword

In one line

super lets a subclass reach its parent: super(...) calls a parent constructor, super.method() runs the parent's version of an overridden method, and super.field reads a parent field that the child has hidden.

Think of it like this

A new shop manager who keeps the old manager's opening routine. Each morning she says 'do everything the old manager did' (unlock, lights on, count the cash), and then adds her own step (play music). She doesn't rewrite the old routine; she calls it and extends it. super.openShop() is 'do what the old manager did'.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

super
A keyword meaning 'my parent class's part of this object'. Used for parent constructors, methods and fields.
super(...)
A call to a parent constructor, written as the first line of a child constructor.
this(...)
A call from one constructor to another constructor of the same class.
Constructor chaining
Constructors calling other constructors (this(...) or super(...)) so each piece of setup is written once.
Field hiding
When a child declares a field with the same name as a parent field. The object then holds both, and which one you read depends on the reference type.
Overridable method
A method a subclass can replace: one that is not private, static or final.

Step by step

01Passing values up with super(...)

A child constructor often receives values that belong to the parent. SavingsAccount(double opening, double rate) gets an opening balance, but balance is a field that belongs to the parent, Account. The child hands it up: super(opening);, then handles its own part: this.rate = rate;.

This keeps each class responsible for its own fields. Account's constructor can validate the balance once, and every subclass benefits.

Main.javawhole filejava
class Account {
    protected double balance;
    Account(double opening) {
        if (opening < 0) throw new IllegalArgumentException("negative");
        balance = opening;
    }
}

class SavingsAccount extends Account {
    private final double rate;
    SavingsAccount(double opening, double rate) {
        super(opening);      // parent validates and stores the balance
        this.rate = rate;    // child stores its own field
    }
}

02Why super(...) must come first

Until Java 25, the language rule was simple: the first statement of a constructor must be super(...) or this(...) (or neither, and the compiler adds super()). Writing anything before it is a compile error.

The reason: until the parent constructor finishes, the parent's part of the object is not set up. If you could call methods on this before super(...), you could observe a half-built parent. The rule makes that impossible.

terminal
$ javac Main.java
── expected output ──
Main.java:2: error: call to super must be first statement in constructor
class Dog extends Animal { Dog() { System.out.println("dog"); super("Rex"); } }
^
1 error

03Extending a method with super.method()

When a child overrides a method (Topic 5.3), the parent's version is still there. Inside the child, super.describe() runs it. A very common shape is 'parent's result plus a bit more': return super.describe() + ", rate=" + rate;.

You can also guard the parent's behaviour: check a condition first, and only call super.withdraw(amount) if it passes. The parent keeps doing the real work; the child adds a rule.

04How super calls compile

A normal call a.sound() compiles to invokevirtual, which looks at the real object at run time and picks the most specific override (Topic 5.4). A super.sound() call compiles to invokespecial naming the parent class directly. The JVM runs exactly that method, with no lookup in the child.

You can see this with javap -c, the JDK's bytecode viewer. It disassembles a compiled .class file into readable instructions.

terminal
$ javac Main.java
javap -c Dog
── expected output ──
class Dog extends Animal {
Dog();
Code:
0: aload_0
1: invokespecial #1 // Method Animal."<init>":()V
4: return
 
java.lang.String sound();
Code:
0: ldc #7 // String Woof
2: areturn
 
java.lang.String parentSound();
Code:
0: aload_0
1: invokespecial #9 // Method Animal.sound:()Ljava/lang/String;
4: areturn
}
Both the inserted super() and super.sound() are invokespecial: a direct, non-virtual call to Animal's code.

05this(...) chains end in super(...)

A class often has several constructors with sensible defaults. Instead of repeating setup, each one calls the most complete one with this(...). Only the last constructor in the chain calls super(...).

You can't write both this(...) and super(...) in one constructor, because then the parent would be initialised twice.

this(...) chains end in super(...)diagram
Rendering diagram…

06The constructor trap: calling overridable methods

Here is a bug that catches experienced developers. Base's constructor calls describe(). Child overrides describe() to print its fields. When you write new Child(), the order is: Child's constructor calls super() → Base's constructor runs → it calls describe() → dynamic dispatch picks Child's version → Child's fields have not been initialised yet (that happens after super() returns) → it prints null and 0.

The rule: a constructor should only call methods the subclass can't override (private, final, or static). Effective Java calls this 'constructors must not invoke overridable methods'.

The constructor trap: calling overridable methodsdiagram
Rendering diagram…

Try it yourself

  1. 1

    Swap the order in describe()

    In the Account example, change describe() to return "rate=" + rate + "; " + super.describe();. Predict the last line before running.

  2. 2

    Fix the constructor trap

    In the trap example, make describe() in Base private (and remove @Override from Child, since it no longer overrides anything). Run it. Base's constructor now prints Base, because a private method can't be overridden, so no dispatch to Child happens.

  3. 3

    Make the field final

    In the trap example, change private String name = "Child"; to private final String name = "Child"; and run again. The first line now shows name=Child but still size=0. A final field initialised with a constant expression is a constant variable, and javac inlines its value at every use, so there is no field read at all. size is not final, so it is still read too early.

Code & diagrams

super(...) and super.method() together New tab
Sign in to run this example in your browser.

Expected output

Account opened with 100.0
Savings rate set to 0.05
Refused: 500.0
Account balance=70.0, rate=0.05
The constructor trap, live New tab

Child's field initializers run only after super() returns, so the first call sees default values.

Sign in to run this example in your browser.

Expected output

Base constructor sees: name=null, size=0
Child constructor sees: name=Child, size=42
Field hiding and super.field New tab

The Kid object holds two label fields. The compile-time type of the expression picks which one you get.

Sign in to run this example in your browser.

Expected output

label         = kid field
this.label    = kid field
super.label   = parent field
(Parent) this = parent field
p.label       = parent field
this(...) chaining down to super(...) New tab
Sign in to run this example in your browser.

Expected output

Food(pizza)
Pizza(int, String)
Pizza(int)
Pizza()
pizza 8 inch, cheese

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Put a statement before super(...)

Write System.out.println("dog"); before super("Rex"); in Dog's constructor.

Main.javawhole filejava
class Animal { Animal(String name) { } }
class Dog extends Animal { Dog() { System.out.println("dog"); super("Rex"); } }
terminal
$ javac --release 17 Main.java
── what you'll see ──
Main.java:2: error: call to super must be first statement in constructor
class Dog extends Animal { Dog() { System.out.println("dog"); super("Rex"); } }
^
1 error

Break #2

Try to skip a generation with super.super

In a grandchild class, write super.super.m(); to reach the grandparent's version.

Main.javawhole filejava
class A { void m() { } }
class B extends A { void m() { } }
class C extends B { void m() { super.super.m(); } }
terminal
$ javac Main.java
── what you'll see ──
Main.java:3: error: <identifier> expected
class C extends B { void m() { super.super.m(); } }
^
Main.java:3: error: not a statement
class C extends B { void m() { super.super.m(); } }
^
2 errors

Myth vs fact

Myth

super is a reference to a separate parent object.

Fact

There is only one object. super means 'this same object, but use the parent class's members'. super can't be stored in a variable or passed around; it's syntax, not a value.

Myth

If I don't write super(...), the parent constructor isn't called.

Fact

The compiler inserts super() for you. Some parent constructor always runs, every time.

Myth

super.method() is dynamically dispatched like any other call.

Fact

It compiles to invokespecial and always runs the direct parent's (or the nearest ancestor's) implementation. Only ordinary calls are virtual.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Flexible constructor bodies. JEP 447 (preview in Java 22) and its follow-ups were finalised in Java 25 (JEP 513): statements that don't touch this may now appear before super(...), for example to validate arguments. Fields of the class being constructed may even be assigned there, which closes the 'overridable method sees null' hole for those fields. On Java 17 and 21 the old rule applies.

  • ▸

    super.m() resolves at compile time to the *nearest* superclass that declares m, but the JVM re-resolves it at link time through the superclass chain (the ACC_SUPER semantics, mandatory since Java 8). So if a library later adds m() to an intermediate class, already compiled code picks it up without recompiling.

  • ▸

    The constant-variable inlining shown in the Try It is real JLS behaviour (JLS 13.1, 4.12.4): final fields of primitive or String type initialised with a constant expression are inlined by javac. That also means changing such a public static final constant in a library requires recompiling every class that uses it.

  • ▸

    Interfaces have their own form: Named.super.name() calls a specific interface's default method (Topic 5.8). It's the only place where X.super syntax is legal.

Remember this

  1. 1

    super(arguments) calls a parent constructor. It must be the first statement in a constructor (up to Java 24; see Pro). If you don't write it, the compiler inserts super() with no arguments, which fails if the parent has no no-argument constructor.

  2. 2

    A constructor can start with super(...) or this(...) (call another constructor of the same class), never both. A chain of this(...) calls must eventually reach a constructor that calls super(...), so the parent is still built exactly once.

  3. 3

    super.method() calls the parent's version of a method, even if the current class overrides it. This is how you *extend* behaviour instead of replacing it: do the parent's work, then add yours (or the other way round).

  4. 4

    super.field reads the parent's field when the child declares a field with the same name (field hiding). Hiding fields is legal but confusing; prefer distinct names.

  5. 5

    super only goes one level up, and it is fixed at compile time. There is no super.super.method(). Unlike normal method calls, a super.method() call is not dynamically dispatched: it always runs the parent class's code.

  6. 6

    Danger: a constructor that calls an overridable method. If the parent constructor calls describe() and the child overrides it, the child's version runs *before the child's fields are initialised*, so it sees null and 0. Keep constructors to private, final, or static helpers.

Explain it without notes

01

What are the three uses of super, and when do you need each one?

02

Why must super(...) be the first statement of a constructor (before Java 25)?

03

Explain why a parent constructor that calls an overridable method can see null fields in the child.

04

How does a super.method() call differ from a normal method call at the bytecode level?

Practice

01

Create Employee(String name, double salary) with describe(), and Manager extends Employee with an extra teamSize. Manager's constructor must use super(...) and its describe() must reuse the parent's.

02

Write a Logger with log(String msg) that prints the message, and a TimestampLogger that overrides log to prefix [t=1], [t=2], … using a counter, then delegates to super.log.

03

Give a class three constructors chained with this(...) so that new Box() produces a 1x1x1 box and new Box(5) a 5x5x5 box. Print the volume of each.

Trade-offs

  • ↔

    Calling super.method() reuses the parent's logic, but ties the child to *when* and *whether* the parent does its work. If the parent's method later starts calling other overridable methods, the child can break (the fragile base class problem).

  • ↔

    Field hiding works, but readers almost always misread it. Use distinct field names, or keep fields private and expose methods, which are polymorphic.

Done when you can

  • Done when you can pass constructor arguments to a parent with super(...).

  • Done when you can extend a parent method with super.method() instead of copying it.

  • Done when you can explain why super(...)/this(...) must come first and that you can't use both.

  • Done when you can spot and fix a constructor that calls an overridable method.

  • Done when you can tell invokespecial from invokevirtual in javap -c output.