this and super Keywords in Java: A Complete Beginner Guide
-
Last Updated: July 23, 2026
-
By: javahandson
-
Series
Learn Java in a easy way
Master this and super keywords in Java with clear examples. Fix parameter shadowing, chain constructors, extend overridden methods, and avoid common mistakes.
Two small words show up everywhere in Java code. You will meet this and super keywords in Java on your very first day with classes, and they keep appearing for years afterwards.
Both look harmless. Both point at the same object too. Yet they aim in opposite directions, and that difference confuses a lot of beginners.
Here is the short version. The word this looks at the current class. The word super looks one level up, at the parent class.
That single sentence covers maybe seventy percent of what you need. The rest lives in the details, which is what this guide unpacks.
We will cover the ground in this order:
You need only basic class syntax to follow along. If you have written a constructor and used extends once, you are ready.

Before touching either keyword, get this picture straight. It explains almost every rule that follows.
Say Dog extends Animal. When you call new Dog(), Java does not create two objects.
It creates one object on the heap. Inside that single block of memory sit the Animal fields and the Dog fields together.
The parent portion gets built first. Then the child portion layers on top of it.
Both keywords reference that one object. Neither creates or copies anything.
What changes is which version of a member the compiler picks:
Think of a two-storey building. You stand on the first floor by default. Typing super simply walks you downstairs.
Keep that image handy. Every example below is a variation on it.
Neither keyword works inside static code. A static method belongs to the class, not to any object.
Since both keywords need an object to reference, the compiler rejects them there. We will revisit this rule later with examples.
Start with this, since it is the simpler of the two. Inside any instance method or constructor, this holds a reference to the object that call is running on.
You rarely need to write it out. Java assumes it for you most of the time.
Look at a plain method with no keyword anywhere:
class Counter {
int count = 0;
void increment() {
count++; // really means: this.count++
}
}The compiler expands that line for you. Writing count++ and this.count++ produce identical bytecode.
So why does the keyword exist at all? Because sometimes the short form becomes ambiguous, and then you must be explicit.
This is the number one use of this in real code. A constructor parameter often shares its name with a field:
class Student {
String name;
int age;
Student(String name, int age) {
name = name; // BUG: parameter assigned to itself
age = age; // BUG: field stays null and 0
}
}That code compiles cleanly. It also does nothing useful, which makes it a nasty bug to spot.
The parameter shadows the field. Both name references point at the parameter, so the field never gets a value.
class Student {
String name;
int age;
Student(String name, int age) {
this.name = name; // field = parameter
this.age = age; // field = parameter
}
}Now the left side says this.name, which is unmistakably the field. The right side stays the parameter.
Read it as “my name gets the incoming name.” That phrasing makes the pattern stick.
| INTERVIEW INSIGHT Interviewers often ask why a constructor leaves fields empty. The answer is parameter shadowing. Without this, the compiler resolves both sides to the nearest declaration, which is the parameter. No warning appears because self-assignment is legal Java. |
Why Not Just Rename the Parameter?
You could write Student(String studentName, int studentAge) instead. Plenty of older codebases do exactly that.
Modern Java style prefers matching names plus this. The parameter name then documents the field it fills, and IDE autocomplete works better.
You can prefix any instance method call with this. Java assumes it when you leave it out:
class Order {
double amount;
void checkout() {
validate(); // same as this.validate()
this.charge(); // explicit, identical result
}
void validate() { System.out.println("Validating " + amount); }
void charge() { System.out.println("Charging " + amount); }
}Both call styles behave the same. Most developers skip the prefix here, since it adds length without adding clarity.
Here the keyword becomes genuinely necessary. Sometimes a method must hand the whole object to somebody else.
class Button {
String label;
Button(String label) {
this.label = label;
}
void register(ClickHandler handler) {
handler.attach(this); // pass this exact button
}
}
class ClickHandler {
void attach(Button b) {
System.out.println("Attached to: " + b.label);
}
}Without the keyword you have no way to name the current object. There is no variable holding it inside the method.
Event listeners, builders, and callback registrations all rely on this pattern constantly.
Return the current object and callers can chain calls together. Builder classes use the trick everywhere:
class Pizza {
String size = "medium";
boolean cheese = false;
boolean olives = false;
Pizza size(String size) {
this.size = size;
return this; // hand the object back
}
Pizza addCheese() {
this.cheese = true;
return this;
}
Pizza addOlives() {
this.olives = true;
return this;
}
}
// Now calls chain naturally:
Pizza order = new Pizza()
.size("large")
.addCheese()
.addOlives();Each method finishes by returning the same object. The next call in the chain simply continues on it.
You will meet this style in StringBuilder, Stream, and most Spring configuration classes.
An inner class has its own this. Reaching the outer object needs a qualified form:
class Outer {
String label = "outer";
class Inner {
String label = "inner";
void print() {
System.out.println(this.label); // inner
System.out.println(Outer.this.label); // outer
}
}
}Write Outer.this to name the enclosing instance. Plain this always means the innermost class.
Anonymous classes and lambdas differ here, and that gap catches people out. A lambda has no this of its own, so this inside a lambda refers to the enclosing class.
Add parentheses and the keyword changes job completely. Now this() calls another constructor in the same class.
Classes often need several constructors. Copy the assignment logic into each one and maintenance gets painful:
class Book {
String title;
String author;
double price;
Book(String title) {
this.title = title;
this.author = "Unknown"; // duplicated
this.price = 0.0; // duplicated
}
Book(String title, String author) {
this.title = title;
this.author = author;
this.price = 0.0; // duplicated again
}
}Three fields, two constructors, and already the assignments repeat. Add a fourth field later and you must edit every constructor.
Point the short constructors at the fullest one instead:
class Book {
String title;
String author;
double price;
Book(String title) {
this(title, "Unknown", 0.0); // delegate
}
Book(String title, String author) {
this(title, author, 0.0); // delegate
}
Book(String title, String author, double price) {
this.title = title; // assignments live here only
this.author = author;
this.price = price;
}
}Only the last constructor touches the fields. Everything else supplies defaults and forwards the call.
Add a fifth field tomorrow and you edit exactly one place. That is the payoff.
A few restrictions apply, and the compiler enforces every one:
class Broken {
Broken() { this(5); }
Broken(int x) { this(); } // compile error: recursive invocation
}Java detects that loop at compile time. No stack overflow at runtime, thankfully.
| INTERVIEW INSIGHT A constructor that calls this() still reaches super() eventually. The chain runs inside the class first, then jumps to the parent from the final constructor. Every object creation ends up calling one parent constructor exactly once. |
You have seen how this points inward. Now turn the arrow around. The super keyword points at the parent class part of the very same object.
Section 2 described the two-storey building. The keyword super is how you walk downstairs to the floor below.
Picture a two-storey building. The ground floor holds parent members. The first floor holds child members. You stand on the first floor by default.
Now say both floors have a room with the same name. Typing the plain name gets you the room on your own floor. Typing super walks you downstairs instead.
That is the whole idea. Nothing more mysterious than that.
One detail matters here. You never create two separate objects. The parent portion and the child portion share a single block of memory on the heap.
So super is not a pointer to some other object sitting elsewhere. It is a compile-time instruction telling Java which version of a member to pick.
Java uses this one keyword for three separate tasks. Each has its own syntax:
Notice the third one has parentheses right after super. Just like this(), that form only works inside a constructor.
The first two forms use a dot. They behave like normal member access, just aimed one level up. You can place them anywhere inside an instance method.
The third form is a different beast entirely. Java treats it as an explicit constructor invocation, and it follows much stricter rules.
Sometimes a child class declares a variable with the same name as one in the parent. Java calls this field hiding. Nothing gets deleted, and both fields still exist.
Look at what happens when names collide. The child name wins by default:
class Vehicle {
String type = "Generic Vehicle";
}
class Bike extends Vehicle {
String type = "Sports Bike";
void printType() {
System.out.println(type); // Sports Bike
System.out.println(this.type); // Sports Bike
}
}Both lines print the child value. The parent field is still there, but you cannot see it from here.
Why does the child win? The compiler looks for the nearest matching name, starting from the current class. It finds type in Bike and stops searching.
Adding this changes nothing either. Both this.type and a bare type resolve the same way, since this points at the current class view.
Now add super and the parent value comes back into reach:
class Bike extends Vehicle {
String type = "Sports Bike";
void printBoth() {
System.out.println(type); // Sports Bike
System.out.println(super.type); // Generic Vehicle
}
}
public class Demo {
public static void main(String[] args) {
new Bike().printBoth();
}
}One object, two values, two labels. That is field hiding in action.
Both Fields Really Do Exist
Some people assume the child field overwrites the parent one. Memory tells a different story.
Bike b = new Bike(); Vehicle v = b; // same object, parent-typed reference System.out.println(b.type); // Sports Bike System.out.println(v.type); // Generic Vehicle
One object, yet two different answers. The reference type decided which field the compiler picked.
A Quick Warning About Field Hiding
Hiding fields is legal, yet it rarely helps real code. Readers get confused, and bugs slip in quietly.
Prefer a different name, or make the parent field private and expose a getter. Save super.field for cases where you truly cannot rename anything.
| INTERVIEW INSIGHT Fields are not polymorphic. A method call picks the version based on the actual object at runtime. A field access picks the version based on the reference type at compile time. So Vehicle v = new Bike(); v.type prints the parent value, not the child one. |
This is the most common use you will meet in daily work. You override a method, yet you still want the parent logic to run.
Overriding wipes out the parent version for that object. Adding super.method() brings it back as a step inside your own version:
class Payment {
void process() {
System.out.println("Validating amount");
System.out.println("Contacting bank");
}
}
class CardPayment extends Payment {
@Override
void process() {
super.process(); // run parent steps first
System.out.println("Charging the card");
}
}Calling process() on a CardPayment now prints all three lines. You reused the parent work instead of copying it.
Copy-pasting the parent lines would work too, at first. Yet the moment someone updates Payment, your duplicated version drifts out of sync.
That is the real value here. One call keeps the shared logic in exactly one place, which is where bug fixes belong.
You will spot this pattern all over real frameworks. Servlet code, Android lifecycle methods, and Spring base classes all lean on it heavily.
You decide where the super call sits. Put it first when the parent sets things up. Put it last when the parent finishes things off.
class AuditedPayment extends Payment {
@Override
void process() {
System.out.println("Writing audit entry");
super.process(); // parent runs after our step
System.out.println("Closing audit entry");
}
}Unlike constructors, method calls place no rule on position. You are free here.
If the child never overrides a method, super.method() and plain method() do the same thing. The super prefix adds nothing.
So keep super for the override case. Elsewhere it just adds noise.
A Real Pattern You Will Meet
Base classes often define a template of steps. Child classes plug into that template rather than rewriting it.
class Report {
void generate() {
System.out.println("Fetching data");
System.out.println("Formatting rows");
}
}
class PdfReport extends Report {
@Override
void generate() {
super.generate();
System.out.println("Rendering to PDF");
}
}
class ExcelReport extends Report {
@Override
void generate() {
super.generate();
System.out.println("Writing to XLSX");
}
}Both children share the fetch and format steps. Neither one duplicates a single line of that logic.
Change the base method later and both reports pick up the change for free. That is inheritance doing useful work.
Say you have three classes in a chain: A, then B extends A, then C extends B. Inside C, super refers to B alone.
There is no super.super in Java. You cannot skip a level and jump straight to A:
class A { void greet() { System.out.println("A"); } }
class B extends A {
@Override void greet() { System.out.println("B"); }
}
class C extends B {
@Override void greet() {
super.greet(); // prints B — legal
// super.super.greet(); // compile error, not valid Java
}
}Java blocks this on purpose. Skipping a middle class would let you bypass logic that B relies on, which breaks encapsulation.
Now we reach the form with parentheses. Here super() is not a reference at all. It is a constructor call.
A child object contains parent fields. Someone has to initialise them, and only the parent constructor knows how.
Java handles this by chaining. Every constructor calls a constructor above it, until the chain reaches Object at the top.
Think about the risk if this did not happen. Your child constructor might read a parent field before anyone assigned it a value.
Chaining removes that danger completely. By the time your child code runs, every field above it already holds a proper value.
Here is something most beginners miss. If you write no constructor call at all, the compiler inserts super() for you:
class Animal {
Animal() {
System.out.println("Animal constructor");
}
}
class Dog extends Animal {
Dog() {
// compiler quietly inserts: super();
System.out.println("Dog constructor");
}
}
// new Dog() prints:
// Animal constructor
// Dog constructorNotice the parent line prints first. The chain runs upward, then bodies execute downward.
The inserted call is always the no-argument version. So a parent with only a parameterised constructor forces you to write super() yourself:
class Employee {
String name;
int id;
Employee(String name, int id) {
this.name = name;
this.id = id;
}
}
class Manager extends Employee {
String department;
Manager(String name, int id, String department) {
super(name, id); // required, no default exists
this.department = department;
}
}Leave out that super call and the code will not compile. The compiler looks for Employee() and finds nothing.
The error message points straight at the constructor, which helps. Still, beginners often try to fix it by assigning parent fields directly in the child.
Avoid that habit. Passing values upward respects whatever validation or setup the parent constructor performs.
Traditionally super() had to be the very first statement in a constructor. That rule kept things safe, since a parent must be ready before child code touches anything.
Java 22 relaxed this through JEP 447. Now you may run some plain statements before the super call, as long as none of them touch the object under construction:
class Order extends Transaction {
Order(int amount) {
// Java 22+ allows validation before super(...)
if (amount <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
super(amount);
}
}Most projects still run older versions, so treat “first statement” as the safe default. Mention the newer rule in interviews only if the topic comes up.
| INTERVIEW INSIGHT A constructor body may contain super(…) or this(…), never both. Using this(…) delegates to another constructor in the same class, and that one eventually reaches super(…). Only one path up the chain is allowed per constructor. |
A three-level example makes the order obvious:
class Grandparent {
Grandparent() { System.out.println("Grandparent"); }
}
class Parent extends Grandparent {
Parent() { System.out.println("Parent"); }
}
class Child extends Parent {
Child() { System.out.println("Child"); }
}
// new Child() prints:
// Grandparent
// Parent
// ChildEach constructor pauses, sends control upward, and resumes only after the parent finishes. Java always builds an object from the top down.
A class with several constructors can route them through one another. Only the final one in that chain calls super() directly.
class Product {
String name;
double price;
String category;
Product(String name) {
this(name, 0.0); // delegate within the class
}
Product(String name, double price) {
this(name, price, "General"); // delegate again
}
Product(String name, double price, String category) {
super(); // finally reaches Object
this.name = name;
this.price = price;
this.category = category;
}
}This keeps assignment logic in a single constructor. The shorter ones simply fill in sensible defaults.
Follow the arrows and you will see each constructor calls exactly one other. No constructor ever runs twice for a single object.
Java 8 gave interfaces default methods, which come with a body. That created a fresh naming problem, and a special super syntax to fix it.
Suppose two unrelated interfaces declare the same default method. A class implementing both leaves the compiler stuck:
interface Walkable {
default void move() { System.out.println("Walking"); }
}
interface Swimmable {
default void move() { System.out.println("Swimming"); }
}
// Will not compile: which move() should Duck inherit?
class Duck implements Walkable, Swimmable { }Java refuses to guess. A class that implements two independent interfaces with the same default method signature must override that method itself.
People call this the diamond problem. Older languages allowed multiple inheritance of state, which created messy ambiguity.
Java sidestepped that for years by banning multiple class inheritance. Default methods brought a milder version of the issue back, so the language added an explicit escape hatch.
The fix uses a qualified form of super. You name the interface, then reach its default body:
class Duck implements Walkable, Swimmable {
@Override
public void move() {
Walkable.super.move(); // choose the walking version
System.out.println("...then quacking");
}
}Plain super would be meaningless here, because there are two candidates. The interface name removes all doubt.
Java does resolve some conflicts on its own. Three rules apply, checked in order:
Classes rank above interfaces, and a derived sub-interface ranks above the interface it extends. Only the third case forces your hand.
| INTERVIEW INSIGHT The class-wins rule is a favourite interview question. If a class extends a parent with move() and also implements an interface with a default move(), the parent class version runs. To use the interface body instead, you must override and call InterfaceName.super.move() by hand. |
The rule sounds abstract until you run it. Here a class and an interface both offer greet():
interface Friendly {
default void greet() { System.out.println("Hello from interface"); }
}
class Base {
public void greet() { System.out.println("Hello from class"); }
}
class Person extends Base implements Friendly {
// no override needed — the class version wins automatically
}
// new Person().greet() prints: Hello from classNo compile error appears, because Java already knows which one to pick. The class method simply outranks the default.
Want the interface version instead? Override greet() in Person and call Friendly.super.greet() explicitly.
A handful of hard rules govern super. Break one and the compiler stops you immediately.
Static methods belong to the class, not to any object. Since super needs an object to point at, the two cannot mix:
class Parent { void show() { System.out.println("Parent"); } }
class Child extends Parent {
static void broken() {
// super.show(); // compile error: non-static context required
}
}Both lines fail for the same reason. A static method has no object, so neither keyword has anything to point at.
A static method can still work with object members, though. Just accept an object as a parameter, then call methods on it normally.
An interface has no superclass, so plain super has nothing to reference there. Only the qualified InterfaceName.super form works, and only from an implementing class.
The parenthesised forms belong to constructors only. Writing super() or this() inside an ordinary method fails to compile.
Beginners sometimes try this when constructor logic looks reusable elsewhere. Move that logic into a normal method instead, then call it from both places.
A child class does not inherit private members. Therefore super cannot reach them either:
class Account {
private double balance = 100;
protected double getBalance() { return balance; }
}
class Savings extends Account {
void check() {
// System.out.println(super.balance); // error: balance is private
System.out.println(super.getBalance()); // fine: protected method
}
}Route your access through a protected or public method, as shown above.
An anonymous class can call super too. It refers to whatever type you extended or implemented on the spot.
Thread worker = new Thread() {
@Override
public void run() {
System.out.println("Before parent run");
super.run(); // Thread's own run()
}
};Inner classes work slightly differently. Plain super still means the inner class parent, not the outer class.
To reach the enclosing instance, use Outer.this as shown back in section 3.6. Plain this always means the innermost class.
Lambdas break the pattern. A lambda has no this of its own, so this inside one refers to the enclosing class instead.
| Situation | Allowed? | Reason |
|---|---|---|
| this.field in an instance method | Yes | An object exists to reference |
| this() as first statement in a constructor | Yes | Delegates within the same class |
| return this from a method | Yes | Enables method chaining |
| super.field in an instance method | Yes | Reads the hidden parent variable |
| super() in a constructor | Yes | Normal constructor chaining |
| this() and super() in one constructor | No | Only one delegation path is allowed |
| super() or this() in a regular method | No | Constructor calls are constructor-only |
| Either keyword anywhere static | No | No object is present |
| super.super.method() | No | Java exposes one level only |
| super.privateField | No | Private members are never inherited |
These slip-ups show up again and again. Spotting them early saves hours of confusion.
Once you add any constructor to a class, Java stops supplying the free no-argument one. Child classes then break with a puzzling message.
Either add an explicit super(…) call in the child, or give the parent a no-argument constructor.
The compiler message usually reads something like “constructor Parent in class Parent cannot be applied to given types.” That wording confuses people, since they never wrote any constructor call.
Remember the hidden super() insert and the message makes sense at once.
Polymorphism covers methods, not variables. Many people assume both follow the same rule, and their output surprises them:
class Parent { String label = "P"; String get() { return "P"; } }
class Child extends Parent {
String label = "C";
@Override String get() { return "C"; }
}
Parent ref = new Child();
System.out.println(ref.label); // P (reference type decides)
System.out.println(ref.get()); // C (object type decides)Read those two lines carefully. The difference catches out plenty of experienced developers too.
A constructor can delegate one way only. Trying both produces an immediate compile error, since each would want to be the first call.
Watch this trap. Calling the overridden name instead of super.name loops forever:
class Logger {
void log() { System.out.println("base log"); }
}
class TimestampLogger extends Logger {
@Override
void log() {
// log(); // WRONG: calls itself, StackOverflowError
super.log(); // RIGHT: calls the parent version
System.out.println("with timestamp");
}
}One missing keyword turns a helpful override into a crash. Always double-check that prefix.
This one bites hard. A parent constructor that calls an overridable method runs the child version, before child fields are ready:
class Base {
Base() {
init(); // dangerous: subclass version runs
}
void init() { System.out.println("Base init"); }
}
class Derived extends Base {
String value = "ready";
@Override
void init() {
System.out.println("Derived init, value = " + value);
}
}
// new Derived() prints: Derived init, value = nullThe field prints as null. Base ran first, and Derived had no chance to assign value yet.
Keep constructors simple to avoid this. Mark such helper methods private or final so nobody can override them.
Both keywords are now covered separately. Placing them side by side makes the boundary obvious.
| Aspect | this | super |
|---|---|---|
| Refers to | Current object, current class view | Parent class part of the same object |
| Field access | this.name reads the current class field | super.name reads the parent field |
| Method call | this.show() runs the current version | super.show() runs the parent version |
| Constructor form | this() calls another constructor in the same class | super() calls a parent constructor |
| Main use for fields | Fixing parameter shadowing | Reading a field the child hides |
| Main use for methods | Passing the object, chaining calls | Extending an overridden method |
| Can be returned | Yes, enables method chaining | No, it is not a real reference value |
| Inner class form | Outer.this | Interface.super |
| Works in static code | No | No |
| Inserted automatically | No | Yes, super() is added if you write nothing |
A single example puts every use together:
class Vehicle {
String type = "Vehicle";
Vehicle(String type) {
this.type = type; // this: fix shadowing
}
void describe() {
System.out.println("A " + type);
}
}
class Car extends Vehicle {
String type = "Car";
Car() {
this("Generic Car"); // this(): delegate in-class
}
Car(String type) {
super("Four Wheeler"); // super(): parent constructor
this.type = type; // this: fix shadowing again
}
@Override
void describe() {
super.describe(); // super: parent method
System.out.println("Specifically a " + this.type);
}
}Trace the flow for new Car(). The no-argument constructor delegates, the parent constructor runs, then the child assignment happens.
Calling describe() prints the parent line first, then the child line. Both keywords cooperate on the same object.
Strip away the detail and you get a simple pair of statements.
Use this when you mean the object you are inside right now. Use super when you mean the version that came before yours.
Everything else in this article follows from those two sentences.
Knowing the syntax is one thing. Knowing when to reach for it matters more.
Deep inheritance trees create fragile code. Composition often reads better, so weigh that option before adding another level.
A useful rule of thumb: two or three levels of inheritance is plenty for most business code. Beyond that, tracing behaviour becomes genuinely painful.
Ask a simple question before extending a class. Does the child truly represent a kind of the parent, or do you just want to borrow a few methods? Borrowing is a job for composition.
| Your goal | Reach for |
|---|---|
| Assign a parameter to a field of the same name | this.field = param |
| Reuse constructor logic inside one class | this(args) |
| Hand the current object to another method | this |
| Enable fluent method chaining | return this |
| Add steps around inherited logic | super.method() in your override |
| Initialise parent fields with values | super(args) in the child constructor |
| Pick between clashing interface defaults | InterfaceName.super.method() |
| Reuse a few unrelated helper methods | Composition, not inheritance |
Most day-to-day super usage falls in the first two rows. The rest show up occasionally, and the last row is a warning sign.
A: Both reference the same object, but the viewpoint differs. The keyword this resolves members starting from the current class, while super starts from the parent class. Only the resolution changes, not the object.
A: A constructor parameter often shares its name with a field, which shadows that field. Writing this.name = name assigns the parameter to the field. Without it, the line becomes a self-assignment and the field keeps its default value.
A: The call this() invokes another constructor in the same class, while super() invokes a parent class constructor. Both must be the first statement, so a single constructor can contain one or the other but never both.
A: Returning the current object lets callers chain method calls together. Each method finishes by handing the same object back, so the next call continues on it. StringBuilder and Stream both use this pattern.
A: No. A static method belongs to the class rather than any object, and both keywords need an object to reference. Pass an object as a parameter instead if the static method needs instance members.
A: Use the qualified form Outer.this, since plain this always refers to the innermost class. Lambdas differ here, because a lambda has no this of its own and inherits the enclosing class instance.
A: The compiler inserts a no-argument super() automatically. If the parent class has no no-argument constructor, compilation fails and you must call super(args) yourself.
A: The parent portion of the object must be initialised before any child code touches it. Java 22 relaxed this through JEP 447, allowing plain statements before super() as long as they do not access the object under construction.
A: No. Java exposes only one level up the hierarchy. Skipping a middle class would bypass logic that class depends on, which breaks encapsulation, so the language forbids it.
A: No. Method calls resolve at runtime based on the actual object type, while field access resolves at compile time based on the reference type. So Parent ref = new Child(); ref.label reads the parent field.
A: Override the method in your class and pick a version using InterfaceName.super.methodName(). Java refuses to guess when two unrelated interfaces declare the same default method signature.
A: The class method wins, since classes rank above interfaces in the resolution rules. To use the interface body instead, override the method and call InterfaceName.super.method() explicitly.
Let us pull everything together. Both keywords reference the same object, and only the viewpoint separates them.
The keyword this means the object you are working inside. Use it to fix parameter shadowing, to pass the object elsewhere, and to return for chaining. Add parentheses and this() reuses another constructor in the same class.
The keyword super means the parent version. Reach for super.field when a child hides a variable. Call super.method() to extend an override rather than replace it. Pass super(args) to hand values up the constructor chain.
Remember the shared limits too. Static code has neither keyword, private members stay out of reach, and Java never offers a second hop upward.
Get these rules under your belt and inheritance stops feeling like guesswork. Your constructors initialise cleanly, and your overrides do exactly what you intended.