The super keyword

A child object is built on top of its parent part, so the parent's constructor must run first. The super keyword is how a child talks to its parent: super(...) calls the parent's constructor, super.method() reuses the parent's version of a method, and super.field reaches a hidden parent field. You'll see the hidden super() that javac inserts, the classic compile error it causes, the exact order constructors run in, and then practise with 10 problems.

Module 7 · OOP Pillars⏱ 22 minIntermediate10 practice problems
1 The story

The validation Riya wrote twice

In the college ERP, Person's constructor checks that a name isn't empty and trims extra spaces. Riya's new Student extends Person constructor sets the name again, with the same checks copied in. Then the rule changes to "capitalise the first letter", and only Person gets the fix.

Student(String name, int roll) { super(name); // Person builds its part this.roll = roll; } one line replaces the copied checks 2nd: Student part roll = 42 1st: Person part name = "Riya" foundation first, then the floor above myphatshala.in
Riya

Sir, Student extends Person, but I still had to copy Person's name checks into Student's constructor.

Arjun Sir

You don't. A Student object has a Person part, and Person's constructor already knows how to build it. Write super(name); as the first line, and Person does its own job, with its own checks.

Riya

Why the first line?

Arjun Sir

Because you build a house from the foundation up. The parent part must be ready before the child's code touches anything. Java enforces that order, and if you forget, it quietly inserts super() for you.

2 Definition

What is the super keyword?

Formal definition (write this in exams)

super is a keyword used inside a subclass to refer to its immediate superclass. It has three uses: super(arguments) invokes a superclass constructor and must be the first statement of a constructor; super.method(...) invokes the superclass's version of a method, even if the subclass overrides it; and super.field accesses a superclass field hidden by a subclass field of the same name. If a constructor does not begin with this(...) or super(...), the compiler inserts a call to super().

In simple words

this means "me"; super means "the parent part of me". Use it to let the parent build its part, or to reuse what the parent already does.

Words you'll meet in this lesson

super(...)Calls a parent constructor. First line of a constructor only.
Implicit super()The no-argument call javac inserts when you don't write one.
super.method()Runs the parent's version of a method from inside the child.
Field hidingA child field with the same name as a parent field; the parent's still exists.
Constructor chainingConstructors calling each other with this(...) or super(...) (Lesson 6.2).
Default constructorThe no-argument constructor javac adds when a class declares none.

super at a glance

super(args)Only inside a constructor, and only as the first statement. Otherwise: error: call to super must be first statement in constructor
Implicit callNo this(...)/super(...) written → javac inserts super();
Parent lacks no-arg constructorThe implicit super() fails: constructor Person in class Person cannot be applied to given types
this(...) and super(...)Not both in one constructor: each must be first. A this(...) chain must end in a constructor that calls super.
OrderConstructors are called child → parent, but their bodies finish parent → child
Static contextsuper needs an object, so it can't be used in a static method

Exam tip: "What is printed first?" questions on constructors always print the topmost parent's constructor message first. And if the parent declares only Person(String), a child constructor without an explicit super(name) doesn't compile, because javac's hidden super() has nothing to call.

3 Types and ways to use it

Three uses of super

super(args)

Call the parent's constructor so it builds its part of the object.

constructors only

super.method()

Reuse the parent's version, then add to it. Most useful with overriding (Lesson 7.4).

reuse, then extend

super.field

Reach a parent field that a child field with the same name hides.

rare; avoid hiding

All three in one program

class Product {
    String label = "product";
    double price;

    Product(double price) {
        this.price = price;
    }

    String describe() {
        return "Price Rs " + price;
    }
}

class Book extends Product {
    String label = "book";              // hides Product's label
    String author;

    Book(double price, String author) {
        super(price);                   // 1. parent constructor
        this.author = author;
    }

    String describe() {
        return super.describe() + " by " + author;   // 2. parent method
    }

    void labels() {
        System.out.println(label + " / " + super.label);   // 3. parent field
    }
}

public class Main {
    public static void main(String[] args) {
        Book b = new Book(399, "R. K. Narayan");
        System.out.println(b.describe());
        b.labels();
    }
}
Price Rs 399.0 by R. K. Narayan book / product
thissuper
Refers toThe current object, as its own classThe parent part of the current object
Constructor callthis(...): another constructor of the same classsuper(...): a constructor of the parent class
Method callthis.m(): the most specific version (may be the child's)super.m(): the parent's version, always
Fieldthis.x: this class's fieldsuper.x: the parent's field, even if hidden
Can be stored or passed?Yes: list.add(this)No: super alone is not an expression

Classic mistake: inside an overriding method, writing salary() when you meant super.salary(). The method calls itself forever and crashes with StackOverflowError. You'll trigger this in the runner below.

4 Step by step

What happens when you write new Student("Riya", 42)

  1. Memory for the whole object is set aside

    One object is created with room for Person's name and Student's roll, all at default values (null, 0).

    The new instruction allocates the object first. Constructors don't create objects; they initialise an object that already exists.
  2. The child constructor starts and immediately calls up

    Student(String name, int roll) {
        super(name);
        this.roll = roll;
    }
    A frame for Student(...) is pushed, and its first instruction pushes a frame for Person(String) on top, both with this pointing at the same object.
  3. The chain reaches Object, then unwinds

    Person's constructor has no explicit super, so javac inserted super(): Object's constructor runs first. Then Person's body sets name.

    In the bytecode (javap -c), every constructor begins with invokespecial of its parent's <init>, even when you didn't write super.
  4. Field initializers, then the child's body

    class Car extends Vehicle {
        int seats = 5;              // runs after super(...) returns
        Car() { System.out.println("Car()"); }
    }
    javac places the code of field initializers right after the super(...) call in every constructor. So the order is: parent constructor, then the child's field initializers, then the rest of the child's constructor body.
  5. Fix the classic compile error

    // Person has only Person(String name)
    class Student extends Person {
        Student(int roll) { ... }          // ✗ hidden super() has no match
        Student(String n, int roll) {
            super(n);                      // ✓ call the constructor that exists
        }
    }
    Either call an existing parent constructor explicitly, or add a no-argument constructor to the parent. The same error appears if the child has no constructor at all, because the default constructor also calls super().
5 See it happen

Called child to parent, built parent to child: both views

Left: the code being run and the console. Right: constructor frames on the stack and the one object on the heap.

new ElectricCar("MH-01-EV-7", 40) 6 steps
What you see new ElectricCar("MH-01-EV-7", 40) ElectricCar(...): super(regNo, 5); Car(...): super(regNo); → Vehicle Car(...): this.seats = seats; ... ElectricCar(...): batteryKwh = 40; ElectricCar e = (the new object); (nothing printed yet) 1. Vehicle part: MH-01-EV-7 2. Car part: 5 seats 3. ElectricCar part: 40 kWh The child's constructor is called first... ...but its first line hands over to Car. Car hands over to Vehicle, which runs first. Vehicle is done; Car finishes its part. Car is done; ElectricCar finishes last. Messages print parent → child. myphatshala.in
Behind the screen Stack Heap main e → #1 ElectricCar(...) Car(...) Vehicle(...) Object() ran first this → #1 this → #1 this → #1 #1 ElectricCar regNo null"MH-01-EV-7" seats 05 batteryKwh 040 Object #1 exists, all defaults. One frame pushed. Car's frame on top, same this → #1. Three frames deep. Vehicle fills its section. Vehicle's frame popped; Car fills seats. Car's frame popped; the child fills its own. All constructor frames gone; e → #1. myphatshala.in
6 Real-world use

super in real systems

Wherever classes extend other classes, super keeps the parent's work in the parent.

Watch it with an example: the order in which constructors run

PremiumOrder extends Order. Creating one runs three constructors. super(…) always runs the parent's part first, so the parent's fields are ready before the child uses them.

new PremiumOrder("A-17") 6 steps
Animated example PremiumOrder(String id) { super(id); priority = true; } Order(String id) { this.id = id; time = now; STACK PremiumOrder(id) Order(id) Object() #1 PremiumOrder id = null id = "A-17" time = null time = 10:42 priority = false priority = true child constructor called super(id) → parent first Order's own super(): Object Order sets id, time back in child: priority fully built ✓ myphatshala.in
InsufficientFunds(..) {super(message);}

1. Custom exceptions

A bank's InsufficientFundsException passes its message up with super(message), so getMessage() and the stack trace work as usual (Lesson 9.5).

new InsufficientFunds("…")→super(message)→getMessage() works
parent: format+ child extras

2. Extending, not replacing, behaviour

An SMS notification's format() calls super.format() for the standard header and body, then trims it to the SMS length. The common format is maintained in one place.

SmsNotification→super.format() + trim→160-char SMS
toString() {super.toString()+ ", limit=…"}

3. toString and equals in hierarchies

A child's toString() often starts with super.toString() so the parent's fields appear in logs without repeating them. The same pattern is used for equals in some codebases.

log(account)→super.toString() + extras→complete log line
BaseEntity()id, createdAt set

4. Base classes that stamp every record

A base entity's no-argument constructor sets a created-at time. Every child constructor runs it through the implicit super(), so no record is ever missing it.

new Order(...)→implicit super()→createdAt filled

Examples are simplified. Frameworks also call constructors for you (for example when loading objects from a database), which is one reason many of them expect a no-argument constructor to exist.

7 Run the code

A manager's pay, built on an employee's

class Employee {
    protected String name;
    protected double basic;

    Employee(String name, double basic) {
        this.name = name;
        this.basic = basic;
        System.out.println("Employee constructor: " + name);
    }

    double salary() {
        return basic;
    }

    String payslip() {
        return name + " | basic " + basic;
    }
}

class Manager extends Employee {
    private double allowance;

    Manager(String name, double basic, double allowance) {
        super(name, basic);
        this.allowance = allowance;
        System.out.println("Manager constructor: allowance " + allowance);
    }

    @Override
    double salary() {
        return super.salary() + allowance;
    }

    @Override
    String payslip() {
        return super.payslip() + " | allowance " + allowance + " | total " + salary();
    }
}

public class Main {
    public static void main(String[] args) {
        Manager m = new Manager("Kiran", 60000, 15000);
        System.out.println(m.payslip());
    }
}
Employee constructor: Kiran Manager constructor: allowance 15000.0 Kiran | basic 60000.0 | allowance 15000.0 | total 75000.0
  • super(name, basic);Employee's constructor builds the Employee part. Its message prints first.
  • @OverrideMarks a method that replaces the parent's version. Lesson 7.4 covers it fully.
  • super.salary() + allowanceReuses the parent's calculation, then adds the manager's part.
  • super.payslip() + ...The common payslip text stays in Employee; Manager only appends.
8 Line by line

super(name) builds the Person part

Watch the Person constructor frame appear on top of Student's, both with this → #1.

Worked examples: real problems, solved and traced in memory

Three real situations where a child class leans on its parent through super. Run each solution, then step through it: watch constructor frames stack up child-first and finish parent-first, and see super.method() run the parent's code on the child object.

Example 1HR · payroll

A manager's salary built on the employee's

Problem statement

Every employee's salary is their basic pay. A manager also gets an allowance. Write Manager so that it passes name and basic up to Employee and computes salary by reusing Employee's calculation.

Input
Manager "Kiran", basic 60000, allowance 15000
Expected output
Kiran: 75000.0

Approach

  1. Manager(...) calls super(name, basic) as its first line.
  2. Manager's salary() returns super.salary() + allowance.
  3. Step through and watch two constructor frames, then two salary() frames, stack up.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 2E-commerce · notifications

An SMS that keeps the standard format

Problem statement

All notifications are formatted as To <customer>: <text>. SMS messages must use the same format, with an [SMS] tag in front. Write SmsNotification without repeating the format code.

Input
to "Meera", text "Order #812 shipped"
Expected output
[SMS] To Meera: Order #812 shipped

Approach

  1. Pass to and text up with super(to, text).
  2. Override format() and build on super.format().
  3. The common format stays in one place: Notification.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 3Ed-tech · course catalogue

Every record gets an ID through the hidden super()

Problem statement

Every stored record needs a unique ID. The base class BaseRecord assigns the next ID in its no-argument constructor. Course extends it but never mentions super. Show that each course still gets an ID.

Input
courses "Core Java", "DSA"
Expected output
101 Core Java
102 DSA

Approach

  1. BaseRecord() increments a static counter and stores it in id.
  2. Course(String title) doesn't write super(); javac inserts it.
  3. Create two courses and watch BaseRecord's constructor run for each.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

9 Quiz and revision

Check yourself

Quick revision

Lesson 7.3 in one screen

  • super(args): call the parent constructor; first statement only.
  • No explicit call → javac inserts super(); fails if the parent has no no-arg constructor.
  • Order: allocate object → parent constructor (up to Object) → child field initializers → child constructor body.
  • Output from constructors appears parent first, child last.
  • super.method(): reuse the parent's version inside an overriding method.
  • super.field: reach a hidden parent field. Better: avoid field hiding.
  • super can't be used in static code, and this(...) and super(...) can't both appear in one constructor.

Interview and viva questions

Why must super() be the first statement in a constructor?

So that the parent part of the object is fully initialised before the child's code uses it. If the child could run code first, it might call an inherited method that relies on parent fields that aren't set yet. (Newer Java versions relax this: previewed from Java 22 and standard from Java 25, statements that don't use the object being built may come before super(...). For Java 8–21 and for exams, treat "first statement" as the rule.)

What happens if you don't write super() in a constructor?

The compiler inserts super(); automatically. If the parent class has no accessible no-argument constructor, compilation fails with "constructor X in class X cannot be applied to given types". The fix is to call an existing parent constructor explicitly with the right arguments.

What is the difference between this() and super()?

this(...) calls another constructor of the same class; super(...) calls a constructor of the parent class. Both must be the first statement, so a constructor can have one or the other. Constructor chains inside a class using this(...) end at a constructor that (explicitly or implicitly) calls super(...).

How many objects are created when you instantiate a subclass?

One. The parent constructors don't create separate parent objects; they initialise the parent's section of the same object. That's why this inside the parent's constructor refers to the child object.

Is it a good idea to call an overridable method from a constructor?

No. The parent's constructor runs before the child's fields are initialised. If it calls a method the child overrides, the child's version runs and sees default values (0, null). That's a classic hidden bug; constructors should call only private or final methods.

Practice problems

Each problem uses one of the three forms of super. Before running, predict the output line by line, especially the order of constructor messages. The last three are business problems.

P1Easy

Pass values up with super(...)

Person has a constructor Person(String name, int age). Write Student extends Person with a constructor that takes name, age and college, passes the first two to Person, and prints all three.

Input
"Riya", 20, "COEP Pune"
Expected output
Riya, 20, COEP Pune
Show solution and explanation
class Person {
    String name;
    int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

class Student extends Person {
    String college;

    Student(String name, int age, String college) {
        super(name, age);
        this.college = college;
    }
}

public class Main {
    public static void main(String[] args) {
        Student s = new Student("Riya", 20, "COEP Pune");
        System.out.println(s.name + ", " + s.age + ", " + s.college);
    }
}
Riya, 20, COEP Pune

How it works: super(name, age) runs Person's constructor on the same object, so Person's two fields are set by Person's own code. Student sets only what is new.

P2Easy

Order of constructor messages

Three classes Grandparent → Parent → Child each print a message in their no-argument constructor. Create a Child and predict the output before running.

Input
new Child()
Expected output
Grandparent constructor
Parent constructor
Child constructor
Show solution and explanation
class Grandparent {
    Grandparent() {
        System.out.println("Grandparent constructor");
    }
}

class Parent extends Grandparent {
    Parent() {
        System.out.println("Parent constructor");
    }
}

class Child extends Parent {
    Child() {
        System.out.println("Child constructor");
    }
}

public class Main {
    public static void main(String[] args) {
        new Child();
    }
}
Grandparent constructor Parent constructor Child constructor

How it works: none of the constructors write super(), so javac inserts it in each. Child's constructor calls Parent's, which calls Grandparent's, which calls Object's. The bodies then complete from the top down: Grandparent, Parent, Child.

P3Easy

Reuse the parent's display with super.method()

Vehicle.details() prints the registration number and wheels. In Car, write details() that prints the same lines plus the number of seats, without copying Vehicle's code.

Input
Car "DL-3C-4567", 4 wheels, 5 seats
Expected output
Reg no: DL-3C-4567
Wheels: 4
Seats: 5
Show solution and explanation
class Vehicle {
    String regNo;
    int wheels;

    void details() {
        System.out.println("Reg no: " + regNo);
        System.out.println("Wheels: " + wheels);
    }
}

class Car extends Vehicle {
    int seats;

    @Override
    void details() {
        super.details();
        System.out.println("Seats: " + seats);
    }
}

public class Main {
    public static void main(String[] args) {
        Car c = new Car();
        c.regNo = "DL-3C-4567";
        c.wheels = 4;
        c.seats = 5;
        c.details();
    }
}
Reg no: DL-3C-4567 Wheels: 4 Seats: 5

How it works: Car replaces details() (overriding, Lesson 7.4), but its first line calls super.details(), which runs Vehicle's version on the same object. If Vehicle's output format changes, Car picks up the change automatically.

P4Easy

Hidden field and super.field

Both Shop and Branch extends Shop declare a field city. Print both values from a method in Branch.

Input
Shop city "Mumbai", Branch city "Nashik"
Expected output
Branch city: Nashik
Head office city: Mumbai
Show solution and explanation
class Shop {
    String city = "Mumbai";
}

class Branch extends Shop {
    String city = "Nashik";

    void show() {
        System.out.println("Branch city: " + city);
        System.out.println("Head office city: " + super.city);
    }
}

public class Main {
    public static void main(String[] args) {
        new Branch().show();
    }
}
Branch city: Nashik Head office city: Mumbai

How it works: a Branch object has two city fields: Shop's and its own. The plain name means Branch's; super.city means Shop's. Hiding fields like this is confusing in real code; a different name (headOfficeCity) would be clearer.

P5Medium

this(...) chain ending in super(...)

Course has Course(String title, int seats). In OnlineCourse extends Course, write two constructors: OnlineCourse(String title) (default 500 seats, platform "Web") using this(...), and OnlineCourse(String title, int seats, String platform) using super(...).

Input
new OnlineCourse("Core Java")
new OnlineCourse("Spring Boot", 120, "App")
Expected output
Course(Core Java, 500)
OnlineCourse(title, seats, platform) done
OnlineCourse(title) done
Core Java | 500 | Web
Course(Spring Boot, 120)
OnlineCourse(title, seats, platform) done
Spring Boot | 120 | App
Show solution and explanation
class Course {
    String title;
    int seats;

    Course(String title, int seats) {
        this.title = title;
        this.seats = seats;
        System.out.println("Course(" + title + ", " + seats + ")");
    }
}

class OnlineCourse extends Course {
    String platform;

    OnlineCourse(String title) {
        this(title, 500, "Web");
        System.out.println("OnlineCourse(title) done");
    }

    OnlineCourse(String title, int seats, String platform) {
        super(title, seats);
        this.platform = platform;
        System.out.println("OnlineCourse(title, seats, platform) done");
    }
}

public class Main {
    public static void main(String[] args) {
        OnlineCourse a = new OnlineCourse("Core Java");
        System.out.println(a.title + " | " + a.seats + " | " + a.platform);
        OnlineCourse b = new OnlineCourse("Spring Boot", 120, "App");
        System.out.println(b.title + " | " + b.seats + " | " + b.platform);
    }
}
Course(Core Java, 500) OnlineCourse(title, seats, platform) done OnlineCourse(title) done Core Java | 500 | Web Course(Spring Boot, 120) OnlineCourse(title, seats, platform) done Spring Boot | 120 | App

How it works: the one-argument constructor delegates to the three-argument one with this(...); that one calls super(...). A constructor can't contain both calls, but a chain can use both, one per constructor. The messages show the order: Course, then the full constructor, then the short one.

P6Medium

Fix the missing no-argument constructor

This code doesn't compile because Product has only Product(String name) and Gift doesn't call it. Fix Gift (not Product) so that every gift's product name is "Gift box", then print it.

Input
Product(String) only; Gift(String message)
Expected output
Gift box: Happy Diwali!
Show solution and explanation
class Product {
    String name;

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

class Gift extends Product {
    String message;

    Gift(String message) {
        super("Gift box");          // the fix: call the constructor that exists
        this.message = message;
    }
}

public class Main {
    public static void main(String[] args) {
        Gift g = new Gift("Happy Diwali!");
        System.out.println(g.name + ": " + g.message);
    }
}
Gift box: Happy Diwali!

How it works: without the super("Gift box") line, javac inserts super(), and Product has no no-argument constructor: "constructor Product in class Product cannot be applied to given types". Calling the existing constructor explicitly keeps Product's rule that every product has a name.

P7Medium

super in a three-level chain

In Basic → Silver → Gold membership classes, each benefits() returns its parent's benefits plus one more, using super.benefits(). Print Gold's benefits.

Input
new Gold().benefits()
Expected output
Gold: free delivery, 5% cashback, priority support
Silver: free delivery, 5% cashback
Show solution and explanation
class Basic {
    String benefits() {
        return "free delivery";
    }
}

class Silver extends Basic {
    @Override
    String benefits() {
        return super.benefits() + ", 5% cashback";
    }
}

class Gold extends Silver {
    @Override
    String benefits() {
        return super.benefits() + ", priority support";
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("Gold: " + new Gold().benefits());
        System.out.println("Silver: " + new Silver().benefits());
    }
}
Gold: free delivery, 5% cashback, priority support Silver: free delivery, 5% cashback

How it works: super means the immediate parent. Gold's super.benefits() calls Silver's, which calls Basic's. Each level adds one item, so changing Basic's benefit updates every tier. (There is no super.super in Java.)

P8Business problem

Fixed deposit account built on Account

Account(String no, double amount) rejects amounts below Rs 1,000. A FixedDeposit also has a tenure in months and a rate, and its summary() must show the parent's summary plus the maturity amount (simple interest). Create an FD of Rs 1,00,000 for 12 months at 7%.

Input
FD-501, Rs 100000, 12 months, 7%
Expected output
FD-501 | principal Rs 100000.0 | 12 months @ 7.0% | maturity Rs 107000.0
Show solution and explanation
class Account {
    private final String no;
    protected final double amount;

    Account(String no, double amount) {
        if (amount < 1000) throw new IllegalArgumentException("Minimum Rs 1000");
        this.no = no;
        this.amount = amount;
    }

    String summary() {
        return no + " | principal Rs " + amount;
    }
}

class FixedDeposit extends Account {
    private final int months;
    private final double ratePercent;

    FixedDeposit(String no, double amount, int months, double ratePercent) {
        super(no, amount);
        this.months = months;
        this.ratePercent = ratePercent;
    }

    double maturity() {
        return amount + amount * ratePercent / 100 * months / 12;
    }

    @Override
    String summary() {
        return super.summary() + " | " + months + " months @ " + ratePercent + "% | maturity Rs " + maturity();
    }
}

public class Main {
    public static void main(String[] args) {
        FixedDeposit fd = new FixedDeposit("FD-501", 100000, 12, 7);
        System.out.println(fd.summary());
    }
}
FD-501 | principal Rs 100000.0 | 12 months @ 7.0% | maturity Rs 107000.0

How it works:

  1. super(no, amount) reuses Account's minimum-amount check; an FD of Rs 500 would be rejected by the parent before the child sets anything.
  2. super.summary() reuses the common text; the child only appends FD details.
  3. Simple interest: 1,00,000 × 7% × 12/12 = 7,000, so maturity is Rs 1,07,000.
P9Business problem

Express delivery charge

A courier's Delivery.charge() is Rs 40 base + Rs 8 per km. ExpressDelivery charges the normal amount plus a 50% surcharge, and NightExpress adds a further flat Rs 100. Use super.charge() at each level and print charges for 12 km.

Input
distance 12 km
Expected output
Normal: Rs 136.0
Express: Rs 204.0
Night express: Rs 304.0
Show solution and explanation
class Delivery {
    protected final int km;

    Delivery(int km) {
        this.km = km;
    }

    double charge() {
        return 40 + 8 * km;
    }
}

class ExpressDelivery extends Delivery {
    ExpressDelivery(int km) {
        super(km);
    }

    @Override
    double charge() {
        return super.charge() * 1.5;
    }
}

class NightExpress extends ExpressDelivery {
    NightExpress(int km) {
        super(km);
    }

    @Override
    double charge() {
        return super.charge() + 100;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("Normal: Rs " + new Delivery(12).charge());
        System.out.println("Express: Rs " + new ExpressDelivery(12).charge());
        System.out.println("Night express: Rs " + new NightExpress(12).charge());
    }
}
Normal: Rs 136.0 Express: Rs 204.0 Night express: Rs 304.0

How it works:

  1. Base charge: 40 + 8 × 12 = 136.
  2. Express: 136 × 1.5 = 204, computed by calling the parent's charge and adding the surcharge.
  3. Night express: Express's 204 + 100 = 304. If the per-km rate changes in Delivery, all three update together.
P10Business problem

Intern payroll with a stipend cap

Employee(String id, double monthlyPay) prints a joining message. An Intern is created with a stipend; if the stipend is above Rs 25,000 it is capped at 25,000 before being passed to the parent. Print the joining messages and both pays.

Input
Employee "E10" 55000; Intern "I07" stipend 30000
Expected output
Joined: E10 at Rs 55000.0
Joined: I07 at Rs 25000.0
E10 pay: 55000.0
I07 pay: 25000.0
Show solution and explanation
class Employee {
    protected final String id;
    protected final double monthlyPay;

    Employee(String id, double monthlyPay) {
        this.id = id;
        this.monthlyPay = monthlyPay;
        System.out.println("Joined: " + id + " at Rs " + monthlyPay);
    }
}

class Intern extends Employee {
    private static final double CAP = 25000;

    Intern(String id, double stipend) {
        super(id, Math.min(stipend, CAP));
    }

    double pay() {
        return monthlyPay;
    }
}

public class Main {
    public static void main(String[] args) {
        Employee e = new Employee("E10", 55000);
        Intern i = new Intern("I07", 30000);
        System.out.println(e.id + " pay: " + e.monthlyPay);
        System.out.println(i.id + " pay: " + i.pay());
    }
}
Joined: E10 at Rs 55000.0 Joined: I07 at Rs 25000.0 E10 pay: 55000.0 I07 pay: 25000.0

How it works:

  1. Code can't run before super(...) in Java 8–21, but an expression inside its arguments can: Math.min(stipend, CAP) is evaluated first, then passed up.
  2. CAP is static, so it can be used there; an instance field of Intern could not be, because the object isn't initialised yet.
  3. The parent's joining message already shows the capped value, 25000.0.

Real-world scenario questions (asked in company interviews)

Constructor order and super calls cause surprisingly many production bugs. Decide your answer first, then open the model answer.

Banking · build failureFresher

"I only added a constructor to the parent"

A teammate adds Account(String accountNo) to the Account class so every account must have a number. Suddenly SavingsAccount and CurrentAccount, which they didn't touch, fail to compile. Explain why and how to fix it.

The interviewer is checking: do you know about the default constructor and the implicit super()?

Show a strong answer
  • Before the change, Account had no constructors, so javac gave it a default no-arg constructor. Once any constructor is declared, that default disappears.
  • The child constructors (or their default constructors) call super() implicitly, which no longer exists, hence "constructor Account in class Account cannot be applied to given types".
  • Fix: give each child a constructor that takes the account number and calls super(accountNo). That also enforces the new rule for every account type.

Avoid: adding an empty Account() just to make it compile. It defeats the "every account has a number" rule.

E-commerce · notifications1–3 years

The footer disappeared from SMS alerts

Notification.format() adds the order ID and a "Reply STOP to unsubscribe" footer required by regulations. SmsNotification overrides format() to shorten the text, and the footer is missing from all SMS messages. What likely happened?

The interviewer is checking: do you know when to extend the parent's behaviour with super.method()?

Show a strong answer
  • The override rebuilt the text from scratch instead of calling super.format(), so the parent's footer logic never runs for SMS.
  • Fix: String full = super.format(); return shorten(full);, with shortening that always keeps the footer.
  • Better design: make the parent's format() final and expose a smaller hook for the body (template method, Lesson 7.6), so no child can skip mandatory parts.

Avoid: copying the footer text into each child. The next regulation change would miss one.

SaaS · reports1–3 years

A null in the constructor that "can't be null"

Report's constructor calls init(). SalesReport overrides init() to use its field private final List<String> regions = List.of("North", "South"), and crashes with a NullPointerException on regions, although the field is initialised right there. Why?

The interviewer is checking: do you know the exact order of constructor execution?

Show a strong answer
  • Order: Report's constructor runs first (via super()), and it calls init(). Dynamic dispatch picks SalesReport's version, but SalesReport's field initializers haven't run yet, so regions is still null.
  • Fix: don't call overridable methods from constructors. Make init() private or final in Report, or do the setup in the child's own constructor after super(...).
  • Static analysis tools flag "overridable method call in constructor" for exactly this reason.

Avoid: "fixing" it with a null check inside init(). The design is the bug.

Fintech · exceptionsFresher

Error logs with "null" as the message

A payment service throws new PaymentDeclinedException("Card expired"), but logs show PaymentDeclinedException: null. The exception class stores the text in its own field msg. What's wrong?

The interviewer is checking: do you know to pass data up with super(...)?

Show a strong answer
  • getMessage() is inherited from Throwable and reads Throwable's own message field. The custom class never passed the text up, so the implicit super() left it null.
  • Fix: PaymentDeclinedException(String message) { super(message); } and remove the duplicate msg field.
  • Also pass the cause with super(message, cause) when wrapping another exception, so stack traces stay complete (Module 9).

Avoid: overriding getMessage() to return the private field. Let the parent do its job.