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(...)orsuper(...)) 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,staticorfinal.
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.
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.
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.
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.
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'.
Try it yourself
- 1
Swap the order in describe()
In the Account example, change
describe()toreturn "rate=" + rate + "; " + super.describe();. Predict the last line before running. - 2
Fix the constructor trap
In the trap example, make
describe()inBaseprivate(and remove@Overridefrom Child, since it no longer overrides anything). Run it. Base's constructor now printsBase, because a private method can't be overridden, so no dispatch to Child happens. - 3
Make the field final
In the trap example, change
private String name = "Child";toprivate final String name = "Child";and run again. The first line now showsname=Childbut stillsize=0. Afinalfield 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.sizeis not final, so it is still read too early.
Code & diagrams
Expected output
Account opened with 100.0
Savings rate set to 0.05
Refused: 500.0
Account balance=70.0, rate=0.05Child's field initializers run only after super() returns, so the first call sees default values.
Expected output
Base constructor sees: name=null, size=0
Child constructor sees: name=Child, size=42The Kid object holds two label fields. The compile-time type of the expression picks which one you get.
Expected output
label = kid field
this.label = kid field
super.label = parent field
(Parent) this = parent field
p.label = parent fieldExpected output
Food(pizza)
Pizza(int, String)
Pizza(int)
Pizza()
pizza 8 inch, cheeseBreak 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.
class Animal { Animal(String name) { } }
class Dog extends Animal { Dog() { System.out.println("dog"); super("Rex"); } }Break #2
Try to skip a generation with super.super
In a grandchild class, write super.super.m(); to reach the grandparent's version.
class A { void m() { } }
class B extends A { void m() { } }
class C extends B { void m() { super.super.m(); } }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
thismay now appear beforesuper(...), 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 declaresm, but the JVM re-resolves it at link time through the superclass chain (theACC_SUPERsemantics, mandatory since Java 8). So if a library later addsm()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):
finalfields of primitive orStringtype initialised with a constant expression are inlined by javac. That also means changing such apublic static finalconstant 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 whereX.supersyntax is legal.
Remember this
- 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 insertssuper()with no arguments, which fails if the parent has no no-argument constructor. - 2
A constructor can start with
super(...)orthis(...)(call another constructor of the same class), never both. A chain ofthis(...)calls must eventually reach a constructor that callssuper(...), so the parent is still built exactly once. - 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
super.fieldreads 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
superonly goes one level up, and it is fixed at compile time. There is nosuper.super.method(). Unlike normal method calls, asuper.method()call is not dynamically dispatched: it always runs the parent class's code. - 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 seesnulland0. Keep constructors to private, final, or static helpers.
Explain it without notes
What are the three uses of super, and when do you need each one?
Why must super(...) be the first statement of a constructor (before Java 25)?
Explain why a parent constructor that calls an overridable method can see null fields in the child.
How does a super.method() call differ from a normal method call at the bytecode level?
Practice
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.
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.
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
invokespecialfrominvokevirtualinjavap -coutput.