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.
Sir, Student extends Person, but I still had to copy Person's name checks into Student's constructor.
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.
Why the first line?
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.
What is the super keyword?
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().
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
this(...) or super(...) (Lesson 6.2).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 call | No this(...)/super(...) written → javac inserts super(); |
| Parent lacks no-arg constructor | The 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. |
| Order | Constructors are called child → parent, but their bodies finish parent → child |
| Static context | super 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.
Three uses of super
super(args)
Call the parent's constructor so it builds its part of the object.
constructors onlysuper.method()
Reuse the parent's version, then add to it. Most useful with overriding (Lesson 7.4).
reuse, then extendsuper.field
Reach a parent field that a child field with the same name hides.
rare; avoid hidingAll 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();
}
}this | super | |
|---|---|---|
| Refers to | The current object, as its own class | The parent part of the current object |
| Constructor call | this(...): another constructor of the same class | super(...): a constructor of the parent class |
| Method call | this.m(): the most specific version (may be the child's) | super.m(): the parent's version, always |
| Field | this.x: this class's field | super.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.
What happens when you write new Student("Riya", 42)
Memory for the whole object is set aside
One object is created with room for Person's
Thenameand Student'sroll, all at default values (null, 0).newinstruction allocates the object first. Constructors don't create objects; they initialise an object that already exists.The child constructor starts and immediately calls up
A frame forStudent(String name, int roll) { super(name); this.roll = roll; }Student(...)is pushed, and its first instruction pushes a frame forPerson(String)on top, both withthispointing at the same object.The chain reaches Object, then unwinds
Person's constructor has no explicit
In the bytecode (super, so javac insertedsuper(): Object's constructor runs first. Then Person's body setsname.javap -c), every constructor begins withinvokespecialof its parent's<init>, even when you didn't writesuper.Field initializers, then the child's body
javac places the code of field initializers right after theclass Car extends Vehicle { int seats = 5; // runs after super(...) returns Car() { System.out.println("Car()"); } }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.Fix the classic compile error
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// 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 } }super().
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- new: Java calls
ElectricCar's constructor, the class you named. - super(regNo, 5): before doing anything else, ElectricCar's constructor calls Car's. Nothing has printed.
- super(regNo): Car's constructor calls Vehicle's. Vehicle's body is the first to complete, so its message prints first.
- Back in Car: Vehicle's constructor returned. Car sets
seatsand prints. - Back in ElectricCar: finally the child sets its own field and prints.
- Done: called child → parent, completed parent → child. The finished object is assigned to
e.
newallocates object #1 with every field at its default, then pushes ElectricCar's constructor frame.super(regNo, 5)pushes Car's constructor frame. Both frames work on the same object.- Car's
super(regNo)pushes Vehicle's frame; Vehicle's own hiddensuper()ran Object's constructor. Vehicle setsregNo. - Vehicle's frame is popped. Car's constructor continues after its
superline and setsseats. - Car's frame is popped. ElectricCar's constructor sets
batteryKwh. - The last constructor frame is popped and main's
ereceives the reference to #1.
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"): memory for all fields (parent's and child's) is created with defaults, then the child constructor starts.
- super(id): its first line calls the parent constructor. If you don't write
super(…), Java insertssuper()for you. - All the way up: Order's constructor implicitly calls
Object()first. The root always finishes first. - Parent's part: back in Order,
idand the creation time are set. - Child's part: only now does PremiumOrder run its own line,
priority = true. It can safely useid, because the parent already set it. - Order of work: top-down: Object → Order → PremiumOrder. Base classes that stamp IDs and timestamps on every record rely on this.
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).
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.
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.
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.
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.
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());
}
}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.
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.
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.
Manager "Kiran", basic 60000, allowance 15000
Kiran: 75000.0
Approach
Manager(...)callssuper(name, basic)as its first line.- Manager's
salary()returnssuper.salary() + allowance. - 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.
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.
to "Meera", text "Order #812 shipped"
[SMS] To Meera: Order #812 shipped
Approach
- Pass
toandtextup withsuper(to, text). - Override
format()and build onsuper.format(). - 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.
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.
courses "Core Java", "DSA"
101 Core Java 102 DSA
Approach
BaseRecord()increments a static counter and stores it inid.Course(String title)doesn't writesuper(); javac inserts it.- 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.
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.supercan't be used in static code, andthis(...)andsuper(...)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.
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.
"Riya", 20, "COEP Pune"
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);
}
}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.
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.
new Child()
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();
}
}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.
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.
Car "DL-3C-4567", 4 wheels, 5 seats
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();
}
}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.
Hidden field and super.field
Both Shop and Branch extends Shop declare a field city. Print both values from a method in Branch.
Shop city "Mumbai", Branch city "Nashik"
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();
}
}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.
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(...).
new OnlineCourse("Core Java")
new OnlineCourse("Spring Boot", 120, "App")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);
}
}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.
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.
Product(String) only; Gift(String message)
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);
}
}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.
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.
new Gold().benefits()
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());
}
}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.)
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%.
FD-501, Rs 100000, 12 months, 7%
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());
}
}How it works:
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.super.summary()reuses the common text; the child only appends FD details.- Simple interest: 1,00,000 × 7% × 12/12 = 7,000, so maturity is Rs 1,07,000.
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.
distance 12 km
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());
}
}How it works:
- Base charge: 40 + 8 × 12 = 136.
- Express: 136 × 1.5 = 204, computed by calling the parent's charge and adding the surcharge.
- Night express: Express's 204 + 100 = 304. If the per-km rate changes in Delivery, all three update together.
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.
Employee "E10" 55000; Intern "I07" stipend 30000
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());
}
}How it works:
- 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. CAPis static, so it can be used there; an instance field of Intern could not be, because the object isn't initialised yet.- 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.
"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.
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.
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 callsinit(). Dynamic dispatch picks SalesReport's version, but SalesReport's field initializers haven't run yet, soregionsis 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 aftersuper(...). - 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.
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 fromThrowableand reads Throwable's own message field. The custom class never passed the text up, so the implicitsuper()left it null.- Fix:
PaymentDeclinedException(String message) { super(message); }and remove the duplicatemsgfield. - 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.