Skip to content
Java polymorphism 5 min read

super Keyword

The super keyword in Java gives you a direct line to the parent (superclass) from inside a child (subclass). You use it to call the parent’s constructor, access the parent’s fields, or invoke a parent method that the child has overridden — all without leaving the subclass.

Why You Need super

When a subclass inherits from a parent class, it can override fields and methods. Without super, there would be no clean way to reach the original parent version of those members. The keyword solves three problems:

  1. Calling the parent constructor from the child constructor.
  2. Accessing a parent field that the child has hidden with a field of the same name.
  3. Calling a parent method that the child has overridden.

Calling the Parent Constructor

Use super(...) as the first statement inside a child constructor to invoke a parent constructor. If you skip it, Java silently inserts super() (the no-arg form) — which will fail to compile if the parent has no no-arg constructor.

class Animal {
    String name;

    Animal(String name) {
        this.name = name;
        System.out.println("Animal constructor called for: " + name);
    }
}

class Dog extends Animal {
    String breed;

    Dog(String name, String breed) {
        super(name);           // Must be the first statement
        this.breed = breed;
        System.out.println("Dog constructor called. Breed: " + breed);
    }
}

public class Main {
    public static void main(String[] args) {
        Dog d = new Dog("Rex", "Labrador");
    }
}

Output:

Animal constructor called for: Rex
Dog constructor called. Breed: Labrador

Note: super(...) and this(...) cannot both appear as the first statement — you can only pick one. They’re mutually exclusive at the start of a constructor.

Accessing a Parent Field

If a child class declares a field with the same name as a parent field, the child’s field shadows the parent’s. Use super.fieldName to read the parent version explicitly.

class Vehicle {
    String type = "Vehicle";
}

class Car extends Vehicle {
    String type = "Car";   // shadows Vehicle.type

    void printTypes() {
        System.out.println("Child type  : " + type);        // "Car"
        System.out.println("Parent type : " + super.type);  // "Vehicle"
    }
}

public class Main {
    public static void main(String[] args) {
        new Car().printTypes();
    }
}

Output:

Child type  : Car
Parent type : Vehicle

Tip: Field shadowing is generally a design smell — prefer unique field names unless you have a very specific reason. But when you inherit code you can’t change, super.field is your escape hatch.

Calling an Overridden Parent Method

This is the most common use of super. When you override a method in a subclass, you sometimes still need the parent’s logic — for example, to extend it rather than fully replace it.

class Shape {
    void draw() {
        System.out.println("Drawing a generic shape");
    }
}

class Circle extends Shape {
    @Override
    void draw() {
        super.draw();   // run the parent logic first
        System.out.println("Drawing a circle on top of it");
    }
}

public class Main {
    public static void main(String[] args) {
        new Circle().draw();
    }
}

Output:

Drawing a generic shape
Drawing a circle on top of it

This pattern is common in GUI frameworks, logging wrappers, and template-method designs — the child extends behaviour without discarding the parent’s.

super in a Multi-Level Hierarchy

super always refers to the immediate parent, not a grandparent or higher ancestor. You cannot chain super.super.

class A {
    void greet() { System.out.println("Hello from A"); }
}

class B extends A {
    @Override
    void greet() {
        super.greet();   // calls A.greet()
        System.out.println("Hello from B");
    }
}

class C extends B {
    @Override
    void greet() {
        super.greet();   // calls B.greet() (which internally calls A.greet())
        System.out.println("Hello from C");
    }
}

public class Main {
    public static void main(String[] args) {
        new C().greet();
    }
}

Output:

Hello from A
Hello from B
Hello from C

Each level calls its immediate parent, creating a natural chain up the hierarchy. You get all three layers without ever doing super.super.

Quick Comparison: this vs super

Featurethissuper
Refers toCurrent objectParent class portion of the object
Constructor callthis(...) — calls sibling constructorsuper(...) — calls parent constructor
Field accessthis.field — current class fieldsuper.field — parent class field
Method callthis.method() — current class methodsuper.method() — parent class method
Usable in static context?NoNo

See this Keyword for the full picture on the other side of this pair.

Warning: Neither super nor this can be used inside a static method or a static block — they both require an active object instance.

super and Constructors — The Hidden Rule

Even when you write no explicit constructor at all, Java still ensures every parent constructor chain runs. This is possible because the compiler auto-inserts super() calls. Understanding this prevents a very common compile error.

class Parent {
    Parent(int x) {   // No no-arg constructor!
        System.out.println("Parent: " + x);
    }
}

class Child extends Parent {
    Child() {
        // Compiler would insert super() here — but Parent has no no-arg constructor!
        // This causes: error: constructor Parent in class Parent cannot be applied
    }
}

Fix: always call super(...) explicitly when the parent lacks a no-arg constructor.

class Child extends Parent {
    Child() {
        super(42);   // explicit call — now it compiles
    }
}

Under the Hood

At the bytecode level, super.method() compiles to an invokespecial instruction, while a normal virtual method call compiles to invokevirtual. The key difference:

  • invokevirtual — the JVM performs dynamic dispatch through the vtable, so the most-derived override runs.
  • invokespecial — the JVM calls the method directly on the specified class, bypassing the vtable lookup entirely.

That is why super.draw() always executes Shape.draw() even if the object’s runtime type is Circle. The JVM is explicitly told “use the parent’s version.” This also applies to super(...) constructor calls — they use invokespecial to directly invoke <init> on the parent class.

This distinction matters in runtime polymorphism: you can rely on super.method() to never be re-dispatched to a deeper subclass override.

Common Mistakes

  • Forgetting super() leads to a compile error when the parent has only parameterized constructors.
  • Calling super in a static method — illegal; super is instance-bound.
  • Trying super.super — Java does not allow skipping levels. Refactor your hierarchy if you find yourself wanting this.
  • Overusing super.method() — if the child’s override is supposed to fully replace the parent’s behaviour, adding super.method() re-introduces code you intentionally replaced. Be deliberate.
Last updated June 13, 2026
Was this helpful?