Super Keyword in Java Explained
backend
9 min read
Stop guessing when to use super() in Java. This guide shows you exactly how the super keyword works with constructors, methods, and fields - with real code you can copy.

Published By: Nelson Djalo | Date: April 8, 2026
If you have ever written extends in Java and then wondered how a child class actually talks to its parent, the super keyword is the answer. It is the shortcut Java gives you to reach "up" into the class you inherited from - whether that is calling the parent constructor, running a parent method, or grabbing a parent field that got shadowed.
Most bugs around super come from not knowing the three places you can use it and the one place you cannot. By the end of this post you will know exactly when to type super, when Java adds it for you, and why your compiler sometimes screams at you about it.
super is a reference to the immediate parent class of the current object. When you write class Car extends Vehicle, inside Car the keyword super points to the Vehicle part of that Car instance. It is not a separate object - every Car is a Vehicle, and super is how you access that inherited half.
Think of it like this. Your Car object has two layers - the Vehicle fields and methods it inherited, plus its own new stuff. this refers to the whole object. super refers only to the inherited layer. That mental model will save you an hour of debugging the first time you hit a field shadowing issue.
If you are new to inheritance in general, start with Java for Beginners before going deeper on this.
There are exactly three things super does. Learn these and you have covered 99% of real-world code.
When a child class is constructed, the parent constructor must run first. You call it with super(...) and it must be the first statement in your constructor.
class Vehicle {
protected String brand;
protected int wheels;
public Vehicle(String brand, int wheels) {
this.brand = brand;
this.wheels = wheels;
}
}
class Car extends Vehicle {
private int doors;
public Car(String brand, int doors) {
super(brand, 4); // must be first line
this.doors = doors;
}
}
If Vehicle had no constructor that accepted those arguments, this would not compile. That is the compiler protecting you - it refuses to build a Car without first building a valid Vehicle.
When a child class overrides a method, super.methodName() lets you call the original parent version. This is the pattern for extending behavior instead of replacing it.
class Vehicle {
public String describe() {
return "A " + wheels + "-wheel " + brand;
}
}
class Car extends Vehicle {
private int doors;
@Override
public String describe() {
return super.describe() + " with " + doors + " doors";
}
}
Calling new Car("Toyota", 4).describe() returns "A 4-wheel Toyota with 4 doors". You reused the parent's logic instead of rewriting it.
If a child class declares a field with the same name as a parent field, the child's version shadows the parent's. super.fieldName gets you back to the parent's copy.
class Vehicle {
protected String type = "generic vehicle";
}
class Car extends Vehicle {
protected String type = "passenger car";
public void printTypes() {
System.out.println("Child type: " + this.type);
System.out.println("Parent type: " + super.type);
}
}
Output:
Child type: passenger car
Parent type: generic vehicle
Field shadowing is almost always a smell - prefer renaming the field. But when you inherit a legacy class you cannot change, super.field is how you escape the shadow.
If you do not write super() yourself, Java inserts a no-argument super() call as the first line of every constructor. This is why code like this just works:
class Vehicle {
public Vehicle() {
System.out.println("Vehicle built");
}
}
class Car extends Vehicle {
public Car() {
System.out.println("Car built");
}
}
new Car() prints both lines because the compiler secretly added super() to Car's constructor. The moment the parent does not have a no-arg constructor, you must call super(...) explicitly or the code will not compile. That is the most common "why won't this compile" moment for students learning inheritance.
These two keywords get confused a lot. Here is the difference in one table.
| Keyword | Refers to | Use it for |
|---|---|---|
this | Current object | Own fields, own methods, chaining to another constructor via this(...) |
super | Parent class part of current object | Parent fields, parent methods, parent constructor via super(...) |
One important rule - you can use this(...) or super(...) in a constructor but not both, and whichever you use must be the first line. For a deeper dive on the other side of this, read The 'this' Keyword in Java.
The classic use case for super is the "extend, do not replace" pattern. Imagine a logging method on a base class that every subclass should still run.
class Vehicle {
public void start() {
System.out.println("Running safety checks");
}
}
class Car extends Vehicle {
@Override
public void start() {
super.start(); // keep the safety checks
System.out.println("Starting engine");
}
}
If you forget super.start() in the child, the safety check never runs and you ship a bug. This is why Spring and other frameworks document when subclasses "must call super" in their Javadocs.
Field access in Java is not polymorphic the way method calls are. This trips up even experienced devs.
class Vehicle {
String label = "vehicle";
}
class Car extends Vehicle {
String label = "car";
}
Vehicle v = new Car();
System.out.println(v.label); // prints "vehicle"
Why? Because the compiler looks at the declared type (Vehicle) not the runtime type (Car) when resolving fields. Method calls resolve at runtime, field access resolves at compile time. If you want the child's label, cast to Car or use a getter method. If you want to avoid this whole mess, do not shadow fields in the first place.
super only reaches up one level. There is no super.super.method() in Java. If you have GrandParent -> Parent -> Child, the Child can only call super.method() which goes to Parent. To hit GrandParent, Parent must expose it.
class Vehicle {
public String id() { return "vehicle"; }
}
class Car extends Vehicle {
@Override
public String id() { return "car"; }
}
class SportsCar extends Car {
@Override
public String id() {
return super.id() + " -> sports"; // goes to Car, not Vehicle
}
}
Calling new SportsCar().id() returns "car -> sports". If you want Vehicle's version, you would need a method on Car like parentId() that returns super.id(), and then SportsCar would call parentId(). This limitation is deliberate - deep inheritance chains that need to skip levels are usually a design smell. If you find yourself fighting this, consider composition or check out Abstract Class vs Interface for alternatives.
A few places where super will bite you if you are not careful.
super refers to the parent part of an instance. Static methods have no instance, so super is illegal inside them.
class Car extends Vehicle {
public static void info() {
super.describe(); // compile error
}
}
If you need to call a parent static method, just call it by the parent class name like Vehicle.staticMethod().
Interfaces do not have a single parent class, so plain super does not work inside default methods. Java 8 introduced a special syntax for calling a specific interface's default method - InterfaceName.super.method().
interface Logger {
default void log() { System.out.println("log"); }
}
interface Auditor {
default void log() { System.out.println("audit"); }
}
class Service implements Logger, Auditor {
@Override
public void log() {
Logger.super.log();
Auditor.super.log();
}
}
This is the only time you qualify super with a type name, and it only exists because a class can implement multiple interfaces with clashing default methods.
The call to super(...) must be the first statement in the constructor. You cannot run validation, log something, or call another helper before it.
public Car(String brand) {
System.out.println("Building car"); // compile error
super(brand, 4);
}
Java 22 added a preview feature allowing statements before super(...) in limited cases, but in every mainstream codebase today, treat "super first" as the rule.
No. If the parent has a no-arg constructor, Java calls super() for you automatically. You only need to write it explicitly when the parent requires arguments or when you want to be clear about what is happening.
No. super always refers to the immediate parent. If you need the grandparent's behavior, the parent class has to expose it through its own method.
Nothing - the parent version simply does not run. This can be a bug or a feature depending on what you want. Read the Javadoc of the method you are overriding to see if the parent author expected you to call super.
No. this refers to the whole current object. super refers only to the parent class portion of it. You use super when the child has shadowed a field or overridden a method and you still need the original.
No. Static methods belong to the class, not an instance, so there is no "parent part of this object" to reference. Call the parent's static method by its class name instead.
super is a small keyword with three clear jobs - call the parent constructor, call an overridden parent method, and read a shadowed parent field. Get those three patterns into your muscle memory and inheritance stops being scary.
Want to go from "I know what super does" to "I can design a real class hierarchy"? The Java Master Class walks you through inheritance, polymorphism, and the rest of OOP with projects you can put on your resume.

Skip the generic recommendations. These 9 books changed how I write code, lead teams, and think about systems - from Clean Code to books most devs haven't heard of.

The exact skills, tools, and learning order to go from zero to hired as a Java full stack developer. Covers Spring Boot, React, databases, Docker, and what employers actually look for.

Abstract class or interface? Most Java devs get this wrong. Here's a clear breakdown with a side-by-side comparison table, code examples, and a simple decision rule.
Join thousands of developers mastering in-demand skills with Amigoscode. Try it free today.