Senior 5 min · March 05, 2026

Java super and this — Silent Failure from Missing super()

A Spring Boot service timed out for 30 seconds silently because super(dataSource) was never called.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • this refers to the current object instance; super refers to the parent class portion.
  • this(...) chains constructors in the same class; super(...) calls parent constructors.
  • Both must be the very first statement in a constructor – no exceptions.
  • this.method() is usually redundant; super.method() explicitly calls an overridden parent method.
  • In interfaces, use InterfaceName.super.method() to call a default method.
  • Performance: constructor delegation via this(...) avoids code duplication but adds a tiny call chain overhead (~1 μs per delegation).
Plain-English First

Imagine you're at a family reunion. 'This' is like pointing at yourself — 'I am the one doing this.' 'Super' is like turning to your parent and saying 'Can you handle that part?' In Java, every class is like a child who inherited traits from a parent. 'this' lets the child refer to its own stuff, and 'super' lets it reach up and tap the parent on the shoulder. That's the whole idea.

Every time you build something meaningful in Java — a payment system, a game engine, a REST API — you're stacking classes on top of each other. A BankAccount becomes a SavingsAccount. A Vehicle becomes a Car. That inheritance chain is powerful, but it creates an immediate problem: inside a child class, how do you tell Java 'I want MY version of this method' versus 'I want the version my parent defined'? That tension is exactly where super and this live.

Without these two keywords, Java would have no way to disambiguate. If a child class and its parent both define a field called 'name', which one does Java use? If a constructor needs to reuse logic from another constructor in the same class, how do you avoid copy-pasting? Super and this are the precision tools that answer both questions cleanly, keeping your code DRY and your inheritance hierarchy honest.

By the end of this article, you'll know exactly when to use this() vs super(), why super() must be the first line in a constructor, how to avoid the most common shadowing bugs that trip up intermediate developers, and how to answer the tricky interview questions that separate people who memorized syntax from people who actually understand Java's object model.

What 'this' Actually Refers To — And Why It Matters More Than You Think

The keyword 'this' is a reference to the current object — the instance that is executing the code right now. That sounds simple until you run into the three distinct jobs it does, each solving a different problem.

First, 'this' disambiguates fields from parameters. When a constructor parameter has the same name as an instance field (which is extremely common and considered good style), Java needs a way to tell them apart. 'this.name' means the field on the object; bare 'name' means the local parameter.

Second, 'this()' chains constructors inside the same class. If you have three constructors with slightly different signatures, you don't want to duplicate the initialization logic. You call 'this(...)' with arguments and let one constructor delegate to another.

Third, 'this' can be passed as an argument when an object needs to hand a reference to itself to another object — a common pattern in event listeners and builders.

Understanding all three uses stops you from writing the classic bug where you assign a parameter back to itself and the field stays null forever.

EmployeeConstructorChaining.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class EmployeeConstructorChaining {

    public static void main(String[] args) {
        // Create an employee with all three fields specified
        Employee senior = new Employee("Priya Sharma", "Engineering", 95000);

        // Create an employee using the two-arg shortcut — salary defaults to 60000
        Employee junior = new Employee("Tom Okafor", "Marketing");

        // Create a bare-bones employee — only name provided
        Employee intern = new Employee("Lena Fischer");

        System.out.println(senior);
        System.out.println(junior);
        System.out.println(intern);
    }
}

class Employee {
    private String name;
    private String department;
    private double salary;

    // ── PRIMARY constructor — all three fields provided ──────────────────────
    public Employee(String name, String department, double salary) {
        // 'this.name' = the field on THIS object
        // bare 'name' = the constructor parameter
        // Without 'this.', you'd be assigning name = name (no-op) and the field stays null
        this.name = name;
        this.department = department;
        this.salary = salary;
    }

    // ── CONVENIENCE constructor — default salary ──────────────────────────────
    public Employee(String name, String department) {
        // this(...) MUST be the very first statement — delegates to the 3-arg constructor
        // This keeps all real initialization logic in ONE place
        this(name, department, 60_000.0);
    }

    // ── MINIMAL constructor — only name, rest use sensible defaults ───────────
    public Employee(String name) {
        this(name, "Unassigned"); // chains to the 2-arg constructor above
    }

    // 'this' used to pass the current object to an external method
    public void registerWithHR(HRSystem hrSystem) {
        hrSystem.register(this); // passing the current Employee instance
    }

    @Override
    public String toString() {
        return String.format("Employee{name='%s', dept='%s', salary=%.0f}",
                name, department, salary);
    }
}

class HRSystem {
    public void register(Employee employee) {
        System.out.println("Registering: " + employee);
    }
}
Output
Employee{name='Priya Sharma', dept='Engineering', salary=95000}
Employee{name='Tom Okafor', dept='Marketing', salary=60000}
Employee{name='Lena Fischer', dept='Unassigned', salary=60000}
Watch Out: The Silent Null Bug
If you write 'name = name' instead of 'this.name = name', Java assigns the parameter to itself. The field on the object stays null. No compile error. No runtime exception until something tries to use that field. Always prefix instance fields with 'this.' inside constructors when the parameter names match.
Production Insight
A senior dev once spent three hours debugging a null address on a customer record.
The constructor had address = address — parameter to itself, field never set.
Rule: always use this.field = parameter when names collide.
Key Takeaway
this has three jobs: disambiguate fields, chain constructors, and pass the current object.
The compiler won't save you from the silent null — only discipline and automation will.
Always prefix instance variables with this. in constructors when parameter names match.

What 'super' Does — Reaching Up the Inheritance Chain with Precision

While 'this' points inward to the current object, 'super' points upward to the parent class. It has two distinct uses that mirror the two uses of 'this' you've already seen: accessing parent members (fields and methods) and calling parent constructors.

When a child class overrides a method, calling that method normally gives you the child's version. But sometimes you genuinely want the parent's behavior — perhaps to extend it rather than replace it. 'super.methodName()' is how you say 'run what the parent defined, then I'll add my own logic on top.'

Super constructor calls ('super(...)') solve a different problem: every object in Java must be fully initialized, including the part inherited from the parent. The parent has a constructor for a reason — it sets up its own fields. If you don't explicitly call 'super(...)', Java silently inserts 'super()' (the no-arg version). If the parent has no no-arg constructor, you get a compile error and people often don't know why.

This is the mechanism that ensures the entire inheritance chain gets properly initialized, from the top-most ancestor down to the concrete child class.

VehicleInheritanceDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
public class VehicleInheritanceDemo {

    public static void main(String[] args) {
        // Create a Car — watch how BOTH constructors fire in order
        Car tesla = new Car("Tesla Model 3", 2023, "Electric");
        tesla.describe();

        System.out.println("---");

        // Create an ElectricCar — three-level inheritance chain
        ElectricCar rivian = new ElectricCar("Rivian R1T", 2024, 314);
        rivian.describe();
        rivian.displayRange();
    }
}

// ── PARENT class — knows about every vehicle ─────────────────────────────────
class Vehicle {
    protected String modelName;  // 'protected' so child classes can access directly
    protected int year;

    public Vehicle(String modelName, int year) {
        this.modelName = modelName;
        this.year = year;
        System.out.println("Vehicle constructor ran for: " + modelName);
    }

    public void describe() {
        // Base-level description — child classes will extend this
        System.out.println(year + " " + modelName);
    }
}

// ── CHILD class — adds engine type on top of what Vehicle already knows ──────
class Car extends Vehicle {
    private String engineType;

    public Car(String modelName, int year, String engineType) {
        // super(...) MUST be first — initializes the Vehicle part of this object
        // If we skip this, Java tries super() with no args — Vehicle has none, compile error
        super(modelName, year);
        this.engineType = engineType; // then we initialize Car-specific state
        System.out.println("Car constructor ran, engine: " + engineType);
    }

    @Override
    public void describe() {
        super.describe();            // reuse the parent's output — don't duplicate it
        System.out.println("Engine type: " + engineType); // then add what Car knows
    }
}

// ── GRANDCHILD class — one more level deep ────────────────────────────────────
class ElectricCar extends Car {
    private int rangeInMiles;

    public ElectricCar(String modelName, int year, int rangeInMiles) {
        // Calls Car(String, int, String) — which in turn calls Vehicle(String, int)
        // The entire chain fires top-to-bottom automatically
        super(modelName, year, "Electric");
        this.rangeInMiles = rangeInMiles;
        System.out.println("ElectricCar constructor ran, range: " + rangeInMiles);
    }

    @Override
    public void describe() {
        super.describe(); // calls Car.describe(), which calls Vehicle.describe()
        System.out.println("Powered by: battery");
    }

    public void displayRange() {
        System.out.println("Range: " + rangeInMiles + " miles on a full charge");
    }
}
Output
Vehicle constructor ran for: Tesla Model 3
Car constructor ran, engine: Electric
2023 Tesla Model 3
Engine type: Electric
---
Vehicle constructor ran for: Rivian R1T
Car constructor ran, engine: Electric
ElectricCar constructor ran, range: 314
2024 Rivian R1T
Engine type: Electric
Powered by: battery
Range: 314 miles on a full charge
Interview Gold: Constructor Execution Order
Java always runs constructors top-down — grandparent first, then parent, then child. But the code in each constructor only runs AFTER super() completes. Run the output above in your head during an interview and you'll instantly spot the pattern. This order is guaranteed by the JVM spec and never changes.
Production Insight
Forgetting super() in a child constructor leads to a compilation error when the parent lacks a no-arg constructor.
The error message points at the child class, not the parent — confusing.
Rule: always explicitly call super(...) when the parent has a parameterized constructor.
Key Takeaway
super(...) must be the first line in a child constructor.
If the parent has no no-arg constructor, you must call super(...) explicitly or you can't compile.
The JVM guarantees top-down initialization — trust it, don't fight it.

super vs this in Method Calls — When to Override vs When to Extend

The real craft comes in deciding when to use 'super.method()' inside an override. There are two philosophies: replace the parent behavior entirely, or extend it.

Replacing means you override and never call super — you've decided the parent's implementation is irrelevant. Extending means you call super first (or last), then add your own logic. The 'describe()' chain you saw in the previous example is the extension pattern.

A concrete rule of thumb: if your child class IS-A more specific version of the parent and the parent's behavior is still valid, extend it with super. If your child class has a fundamentally different implementation that shares only the method signature, replace it.

There's also a subtlety with field access. If a child class declares a field with the same name as a parent field (called shadowing), 'this.fieldName' gives the child's version and 'super.fieldName' gives the parent's. This is almost always a design mistake — but knowing what 'super.field' does helps you debug code you didn't write.

PaymentProcessorDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
public class PaymentProcessorDemo {

    public static void main(String[] args) {
        System.out.println("=== Standard Payment ===");
        PaymentProcessor standard = new PaymentProcessor("Visa", 250.00);
        standard.processPayment();

        System.out.println();
        System.out.println("=== Fraud-Checked Payment ===");
        // FraudCheckedPayment EXTENDS the base process — adds a check, keeps the rest
        FraudCheckedPayment secured = new FraudCheckedPayment("Mastercard", 4500.00, "US");
        secured.processPayment();

        System.out.println();
        System.out.println("=== Crypto Payment (full override) ===");
        // CryptoPayment REPLACES the process entirely — calls super for logging only
        CryptoPayment crypto = new CryptoPayment("BTC", 0.05);
        crypto.processPayment();
    }
}

class PaymentProcessor {
    protected String paymentMethod;
    protected double amount;

    public PaymentProcessor(String paymentMethod, double amount) {
        this.paymentMethod = paymentMethod;
        this.amount = amount;
    }

    public void processPayment() {
        // Core logic every payment processor shares
        System.out.println("Processing " + paymentMethod + " payment of $" + amount);
        System.out.println("Contacting payment gateway...");
        System.out.println("Payment authorised.");
    }

    // A shared utility method child classes can call via super
    protected void logTransaction() {
        System.out.println("[LOG] " + paymentMethod + " $" + amount + " recorded.");
    }
}

// EXTENDS the base payment — adds fraud check BEFORE delegating to parent
class FraudCheckedPayment extends PaymentProcessor {
    private String originCountry;

    public FraudCheckedPayment(String paymentMethod, double amount, String originCountry) {
        super(paymentMethod, amount); // initialise the PaymentProcessor part
        this.originCountry = originCountry;
    }

    @Override
    public void processPayment() {
        // Do the extra work first, then let the parent handle the rest
        System.out.println("Running fraud check for origin: " + originCountry);
        if (amount > 3000 && !originCountry.equals("US")) {
            System.out.println("Flagged for manual review — payment held.");
            return; // short-circuit: don't proceed to parent logic
        }
        System.out.println("Fraud check passed.");
        super.processPayment(); // delegate standard processing to parent
    }
}

// REPLACES the base payment process entirely — only borrows logging
class CryptoPayment extends PaymentProcessor {
    public CryptoPayment(String cryptoCurrency, double coinAmount) {
        // We reuse the parent constructor to store values, but the process is custom
        super(cryptoCurrency, coinAmount);
    }

    @Override
    public void processPayment() {
        // Completely different logic — no call to super.processPayment()
        System.out.println("Broadcasting " + amount + " " + paymentMethod + " to blockchain...");
        System.out.println("Waiting for 3 confirmations...");
        System.out.println("Transaction confirmed on-chain.");
        super.logTransaction(); // but we DO reuse the parent's logging utility
    }
}
Output
=== Standard Payment ===
Processing Visa payment of $250.0
Contacting payment gateway...
Payment authorised.
=== Fraud-Checked Payment ===
Running fraud check for origin: US
Fraud check passed.
Processing Mastercard payment of $4500.0
Contacting payment gateway...
Payment authorised.
=== Crypto Payment (full override) ===
Broadcasting 0.05 BTC to blockchain...
Waiting for 3 confirmations...
Transaction confirmed on-chain.
[LOG] BTC $0.05 recorded.
Pro Tip: The Template Method Pattern
The pattern in FraudCheckedPayment — doing something extra then calling super — is the foundation of the Template Method design pattern. The parent defines the skeleton, children customise steps. Once you see this, you'll spot it everywhere in frameworks like Spring and Android's Activity lifecycle.
Production Insight
A common code review comment: 'Why is there no super.method() call?'
If the child completely replaces the parent logic, be explicit about it in comments.
Rule: document whether you are extending or replacing — future maintainers will thank you.
Key Takeaway
Override methods with super.method() when you want to extend; skip it to replace.
Field shadowing is a design smell — avoid it by using this consistently.
The Template Method pattern relies on super calls for extensibility.

Gotchas, Edge Cases and the Rules Java Enforces Non-Negotiably

Two rules in Java are compiler-enforced with zero flexibility, and understanding WHY they exist makes them easy to remember forever.

Rule 1: 'super()' or 'this()' must be the very first statement in a constructor. No exceptions. The reason: Java needs the entire object — including the inherited part — to be initialized before any of your code runs. If you could call super() halfway through, the parent's fields might not exist yet when your code in the lines above tried to use them. The compiler prevents that class of bug entirely.

Rule 2: You cannot use both 'this()' and 'super()' in the same constructor. They're both required to be first — so they can't coexist. If you need to chain constructors and also call a parent constructor, arrange your this() chain so that the final constructor in the chain is the one that calls super(). This is the standard pattern in production Java code.

There's also a subtlety with 'this' in static contexts: you simply can't use it. Static methods belong to the class, not any instance. There is no 'current object' in a static context, so 'this' has no meaning. The compiler will tell you so immediately.

ConstructorRulesDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class ConstructorRulesDemo {
    public static void main(String[] args) {
        // Demonstrate a correctly structured multi-level constructor chain
        Subscription basic = new Subscription("Alice");
        Subscription premium = new Subscription("Bob", "Premium");
        Subscription annual = new Subscription("Carol", "Premium", 12);

        System.out.println(basic);
        System.out.println(premium);
        System.out.println(annual);
    }
}

class Account {
    protected String ownerName;

    public Account(String ownerName) {
        this.ownerName = ownerName;
        System.out.println("Account created for: " + ownerName);
    }
}

class Subscription extends Account {
    private String tier;
    private int durationMonths;

    // ── MINIMAL constructor — chains to the 2-arg version via this() ──────────
    public Subscription(String ownerName) {
        this(ownerName, "Basic"); // this() must be first — chains downward
        // You cannot call super() here too — only one chain-start per constructor
    }

    // ── MID constructor — chains to the full 3-arg version ───────────────────
    public Subscription(String ownerName, String tier) {
        this(ownerName, tier, 1); // delegates again — still no super() here
    }

    // ── FULL constructor — this is the only one that calls super() ────────────
    public Subscription(String ownerName, String tier, int durationMonths) {
        super(ownerName); // super() is first here — Account gets initialized
        // Only NOW can we safely set Subscription-specific fields
        this.tier = tier;
        this.durationMonths = durationMonths;
    }

    @Override
    public String toString() {
        return String.format("Subscription{owner='%s', tier='%s', months=%d}",
                ownerName, tier, durationMonths);
    }

    // ── ILLUSTRATING: 'this' cannot be used in static methods ────────────────
    public static String getDefaultTier() {
        // return this.tier; // COMPILE ERROR: 'this' cannot be referenced from a static context
        return "Basic"; // correct — no 'this' in static methods
    }
}
Output
Account created for: Alice
Account created for: Bob
Account created for: Carol
Subscription{owner='Alice', tier='Basic', months=1}
Subscription{owner='Bob', tier='Premium', months=1}
Subscription{owner='Carol', tier='Premium', months=12}
Watch Out: The Hidden super() Java Inserts
If you don't write any super() call in your constructor, Java silently inserts 'super()' as the first line. This is fine when the parent has a no-arg constructor. But the moment the parent only defines a parameterized constructor, the silent super() fails to compile. The error message ('constructor X() is undefined') confuses beginners because they didn't write that call — Java did. Fix: always explicitly write your super() call with the right arguments.
Production Insight
A common mistake: adding a parameterized constructor to a base class without adding a no-arg constructor.
All existing child classes that relied on implicit super() suddenly break.
Rule: if your base class has parameterized constructors, either keep a no-arg constructor or update all children.
Key Takeaway
super() and this() must be first – no exceptions.
They cannot coexist – chain via this() until the final constructor calls super().
this is illegal in static contexts – the compiler gives a clear error.

Beyond Classes: Using super to Call Interface Default Methods (Java 8+)

Java 8 introduced default methods in interfaces, allowing new methods to be added without breaking existing implementations. This created a new use case for super: calling the default implementation from a specific interface when a class implements multiple interfaces that define the same default method, or when the class overrides it.

The syntax is InterfaceName.super.methodName(). This is different from super.methodName(), which calls the parent class's version. When you have a diamond problem – two interfaces providing the same default method – you must resolve the ambiguity by overriding the method and explicitly choosing which default to invoke.

This feature is especially useful in mixin-like designs and when evolving APIs. Understanding it also helps you avoid the pitfall of accidentally calling the wrong super when a class extends a parent AND implements an interface with a same-named default.

InterfaceSuperDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
interface Logger {
    default void log(String message) {
        System.out.println("[Default Logger]: " + message);
    }
}

interface TimestampLogger {
    default void log(String message) {
        System.out.println("[Timestamped]: " + java.time.Instant.now() + " " + message);
    }
}

// Class implements both — must override log() to resolve conflict
class ApplicationLogger implements Logger, TimestampLogger {
    @Override
    public void log(String message) {
        // We want to use the timestamped version
        TimestampLogger.super.log(message);
        // Optionally add more logic
        System.out.println("[Audit]: Logged at " + java.time.Instant.now());
    }
}

public class InterfaceSuperDemo {
    public static void main(String[] args) {
        ApplicationLogger appLog = new ApplicationLogger();
        appLog.log("System started");
    }
}
Output
[Timestamped]: 2026-04-22T15:30:00Z System started
[Audit]: Logged at 2026-04-22T15:30:00Z
Mental Model: Interface.super vs super
  • super.method() calls the method from the direct parent class.
  • InterfaceName.super.method() calls the default method from that specific interface.
  • You cannot use super to call an interface's default method directly — you must prefix it with the interface name.
  • This syntax only works inside an overriding method that resolves a conflict.
Production Insight
Using InterfaceName.super incorrectly is rare but dangerous: if the interface later removes the default method, your code breaks at compile time.
Always add a unit test that invokes the method to catch such API changes.
Rule: prefer composition over inheritance, but when you must use default methods, test the resolution explicitly.
Key Takeaway
InterfaceName.super.method() calls a specific interface's default.
This resolves diamond conflicts when multiple interfaces define the same default.
Without it, you'd have to copy-paste the implementation – defeating the purpose of defaults.
● Production incidentPOST-MORTEMseverity: high

The Silent Parent Initialization: A Service That Never Connected to the Database

Symptom
A Spring Boot service reported healthy but all database queries timed out after 30 seconds. No exceptions, no errors – just infinite hangs.
Assumption
The team assumed the database URL was wrong or the network was down. They spent three hours debugging network connectivity before checking the code.
Root cause
The child class OrderService extended a base AbstractDatabaseService that had a constructor with parameters (DataSource). The child's constructor was written as public OrderService(DataSource dataSource) { this.dataSource = dataSource; } – it never called super(dataSource). The parent's constructor, which set up the connection pool, never executed. The field dataSource in the parent remained null, but the child shadowed it, so no NPE – just silent failure.
Fix
Changed the child constructor to public OrderService(DataSource dataSource) { super(dataSource); this.dataSource = dataSource; }. This ensured the parent's initialization ran as the first statement.
Key lesson
  • Every constructor in Java must eventually call a parent constructor, either explicitly or implicitly.
  • If the parent class has a parameterized constructor and no no-arg constructor, the child MUST call super(...) explicitly – otherwise compilation fails.
  • When a child declares a field with the same name as the parent, it shadows the parent field; the parent's constructor may set the parent field while the child's field stays null.
  • Always check constructor chains when debugging mysterious null or default-zero values after instantiation.
Production debug guideSymptom → Action guide for the most common runtime problems4 entries
Symptom · 01
Instance field is null after constructor runs, even though it was assigned
Fix
Check for parameter shadowing: ensure the assignment uses this.fieldName = fieldName, not fieldName = name (which assigns parameter to itself). Enable IDE inspection for 'Variable assigned to itself'.
Symptom · 02
Compile error: 'constructor X() is undefined' when no constructor is explicitly called
Fix
The parent class likely lacks a no-arg constructor. Add an explicit super(...) call in the child constructor with the required arguments. Review the parent constructor signatures.
Symptom · 03
Overridden method behaves differently than expected, not calling parent logic
Fix
Check if the override calls super.method(). If missing, the parent's behavior is completely replaced. Add super.method() at the appropriate place to extend instead of replace.
Symptom · 04
In a class implementing multiple interfaces, a default method call produces wrong output
Fix
Use InterfaceName.super.defaultMethod() to specify which interface's default implementation to invoke. Without the interface name prefix, Java calls the most specific override.
★ Quick Debug Cheat Sheet: `super` & `this`Use these commands and checks when you suspect a super/this issue in your code.
Field stays null after constructor
Immediate action
Open the constructor and check if `this.field = field` is used
Commands
IntelliJ: Code → Inspect Code → 'Self-assignment'
`javap -c YourClass.class` to verify the `putfield` instruction uses the right field slot
Fix now
Change name = name to this.name = name
Child class doesn't compile: 'constructor X() is undefined'+
Immediate action
Check parent class for constructors – it probably lacks a no-arg constructor
Commands
`javap -c ParentClass.class` to list constructors
Search for `super(` in child constructor – missing?
Fix now
Add super(requiredArgs) as the first line in child constructor
Default method from interface not called as expected+
Immediate action
Check the class signature and see if it overrides the default method
Commands
`javap -v YourClass.class | grep 'InterfaceName'` to verify the method table
In IntelliJ, Ctrl+Click on the method name to navigate to the implementation
Fix now
Add InterfaceName.super.methodCall() inside the overriding method
Comparison: `this` vs `super` in Java
Feature / Aspectthissuper
What it refers toCurrent instance of the classParent class's portion of the current object
Constructor call syntaxthis(...) — calls another constructor in same classsuper(...) — calls a constructor in the parent class
Position in constructorMust be the very first statementMust be the very first statement
Can both appear in one constructor?No — only one of them can be firstNo — only one of them can be first
Method call usagethis.method() — usually redundant, but used for claritysuper.method() — explicitly calls overridden parent version
Field access usagethis.field — resolves shadowing with local variablessuper.field — accesses parent's shadowed field (rare, avoid)
Valid in static methods?No — compile errorNo — compile error
Can be passed as argument?Yes — 'this' passes current object referenceNo — 'super' is not an object reference, can't be passed
Common real-world useConstructor chaining, disambiguating fieldsExtending overridden methods, initializing parent state
Interface default method callNot applicableInterfaceName.super.method() — calls specific interface default

Key takeaways

1
'this' has three jobs
disambiguate instance fields from parameters, chain constructors within the same class via this(...), and pass the current object as a reference — each solves a distinct problem.
2
'super()' must be the first line of a child constructor because the JVM needs the parent portion of the object fully initialized before any child code touches it
this is a JVM guarantee, not a stylistic rule.
3
When you override a method, you choose between two patterns
call super.method() to extend the parent's behavior, or skip it entirely to replace it. Knowing which to choose is what separates a well-designed inheritance hierarchy from a fragile one.
4
If you write multiple constructors, put all real initialization logic in the most-complete constructor and have shorter constructors delegate with this(...). The fullest constructor is the only one that calls super(...). This keeps your initialization logic in one place and makes future changes trivial.
5
Java 8+ allows InterfaceName.super.method() to call a specific interface's default method
essential for resolving diamond conflicts in multiple interface inheritance.

Common mistakes to avoid

5 patterns
×

Assigning parameter to itself instead of the field

Symptom
Instance field stays null or default value after constructor runs, even though you 'assigned' it. No compile error, no runtime exception until the field is used.
Fix
Always prefix the instance field with this. when the parameter and field have the same name: this.name = name. Modern IDEs warn about 'variable assigned to itself' – enable that inspection.
×

Calling a method before super() in a constructor

Symptom
Compilation fails with 'call to super must be first statement in constructor' even though you put a simple log line before super().
Fix
Move any validation or preparation logic into the parent constructor, or refactor so the child constructor's first line is always super(...). If you absolutely need preprocessing, use a static factory method instead.
×

Expecting implicit super() when the parent has no no-arg constructor

Symptom
Child class fails to compile with 'constructor X() is undefined' pointing at the child. The developer didn't write any constructor call, but Java inserted a non-existent super().
Fix
Add an explicit super(requiredArgs) call in every child constructor. Alternatively, add a no-arg constructor back to the parent class (if safe).
×

Using `this` in a static context

Symptom
Compilation error: 'non-static variable this cannot be referenced from a static context'. The developer mistakenly tried to access instance fields from a static method.
Fix
Remove the this reference – pass the instance as a parameter or convert the method to an instance method.
×

Confusing `super.method()` with `InterfaceName.super.method()`

Symptom
In a class that extends a parent and implements an interface, calling super.method() when you intended to call the interface's default method leads to wrong behaviour.
Fix
Use InterfaceName.super.method() to target the interface's default implementation. Reserve super.method() for the parent class's override.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Can you call this() and super() in the same constructor? Why or why not,...
Q02SENIOR
What happens if you don't write any super() call in a child constructor ...
Q03SENIOR
If a parent class and child class both declare a field called 'status', ...
Q04SENIOR
In Java 8+, how do you call a specific interface's default method from a...
Q05SENIOR
You have a class hierarchy where the parent constructor does heavy initi...
Q01 of 05SENIOR

Can you call this() and super() in the same constructor? Why or why not, and how do you structure a multi-level inheritance chain that uses both constructor delegation and parent initialization?

ANSWER
No, you cannot call both in the same constructor because both must be the very first statement. Java allows only one first statement. The standard pattern is to have a chain of this(...) calls ending in a 'fullest' constructor that calls super(...). For example: public MyClass(String a) { this(a, "default"); } public MyClass(String a, String b) { super(a); this.b = b; }. This keeps initialization in one place while satisfying the constraint.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
What is the difference between super() and super.method() in Java?
02
Can I use 'this' inside a static method in Java?
03
Why must super() always be the first line in a constructor — can't Java just figure it out?
04
Can I call super() inside a method that is not the constructor?
05
What is the syntax to call an interface's default method from an overriding method?
06
Can I use 'super' inside a lambda expression that appears in a child class?
🔥

That's OOP Concepts. Mark it forged?

5 min read · try the examples if you haven't

Previous
Method Overriding in Java
11 / 16 · OOP Concepts
Next
static Keyword in Java