SECTION 1
Historical Context & Motivation
Imagine you are building a video game with dozens of character types — warriors, mages, archers, and healers. Each character has a name, health points, and a method to take damage. Without some way to share that common code, you would copy and paste the same fields and methods into every single class. That is tedious, error-prone, and a nightmare to maintain. Inheritance was invented to solve exactly this problem: it lets one class automatically receive the fields and methods of another, so you only write the shared code once.
The idea of inheritance did not appear overnight. It grew out of decades of research into how programmers can organize and reuse code more effectively. The timeline below highlights the key milestones that led to Java's inheritance model and the super keyword.
1967 • Simula 67 Introduces Classes & Inheritance
1972 • Smalltalk Refines the Concept
1983 • C++ Brings OOP to the Mainstream
1995 • Java Launches with Single Inheritance & super
The central question inheritance answers is: How can we write shared behavior once and let many classes reuse and customize it? Java's answer involves the extends keyword to establish a parent-child relationship and the super keyword to reach back into the parent class when needed.
SECTION 2
Core Principles & Definitions
Before diving into code, let's nail down the key vocabulary. Understanding these terms clearly will make everything else in the lesson click.
Superclass (Parent Class)
class Dog extends Animal, the superclass is Animal.Subclass (Child Class)
Dog is the subclass.extends Keyword
super Keyword
super() to call a superclass constructor and super.methodName() to call a superclass method from the subclass.Method Overriding
KEY TAKEAWAY
super to call back to the original steps whenever you want the classic flavor.One important rule to remember: the super() call inside a constructor must be the very first statement. If you don't write it explicitly, Java inserts super() (the no-argument version) for you behind the scenes. This guarantees that the parent is always initialized before the child adds anything new.
SECTION 3
Visual Explanation — The Inheritance Hierarchy
The diagram below illustrates a simple class hierarchy. An Animal superclass holds shared fields and methods. Two subclasses, Dog and Cat, extend it, inheriting everything and adding their own specialized behavior. Arrows point from child to parent to show the "extends" relationship.
Dog and Cat inherit name, age, and the speak() method from Animal. Each subclass calls super(name, age) in its constructor and overrides speak() with its own version.Notice that the subclasses do not redeclare name or age. Those fields are inherited automatically. The constructor in each subclass uses super(name, age) to pass the values up to Animal's constructor, which handles initializing them. This keeps the code DRY (Don't Repeat Yourself) — a core principle of good software design.
SECTION 4
How super Works Under the Hood
Two Uses of super
The super keyword has exactly two uses in Java. First, super() calls a superclass constructor. You place it as the first line of a subclass constructor and pass the arguments the parent constructor expects. Second, super.methodName() calls a superclass method. This is useful when you override a method but still want to reuse the parent's version as part of your new implementation.
Constructor Chaining
When you create a new Dog object, Java does not just run the Dog constructor in isolation. Instead, it follows a chain: the Dog constructor calls super(), which runs the Animal constructor first. The Animal constructor itself implicitly calls super() on Object (Java's built-in root class). This is called constructor chaining, and it ensures that every level of the hierarchy is properly initialized from top to bottom.
super() calls going up the chain. The green arrows show each constructor finishing and returning control down to the subclass. The superclass is always initialized before the subclass.Calling super.method()
Sometimes when you override a method, you don't want to replace the parent's behavior entirely — you want to add to it. For example, suppose Animal.speak() returns "I am an animal". In Dog, you could write:
Code Snippet
@Override
public String speak() {
return super.speak() + " and I bark!";
}Here, super.speak() calls the Animal version of the method, and the subclass adds its own piece. The result would be "I am an animal and I bark!". Without super, calling speak() inside Dog would recursively call the Dog version — which is not what you want.
SECTION 5
Rules, Patterns, and Common Mistakes
Now that you understand the mechanics, let's catalog the specific rules Java enforces and the common mistakes beginners make. The table below is a handy reference you can come back to while coding.
| Rule | What It Means | What Happens If You Break It |
|---|---|---|
super() must be line 1 | In a subclass constructor, the call to super() must appear before any other statement. | Compiler error: "call to super must be first statement in constructor." |
| Arguments must match | The arguments in super(...) must match one of the superclass's constructor signatures. | Compiler error: "no suitable constructor found." |
Implicit super() | If you omit super(), Java auto-inserts the no-argument version. | If the superclass has no no-arg constructor, you get a compiler error. |
Can't use super in static context | super refers to an instance, so it cannot appear in a static method. | Compiler error: "non-static variable super cannot be referenced from a static context." |
| Single inheritance only | A Java class can extend only one superclass (no multiple inheritance of classes). | Compiler error. Use interfaces if you need multiple "types." |
Key rules for using extends and super in Java
Common Mistake Alert
super(args), Java tries to call super() automatically — and fails because no such constructor exists. Always either provide a no-arg constructor in the superclass or explicitly call super(args) in every subclass constructor.SECTION 6
Worked Example — Building an Animal Hierarchy
Let's walk through a complete example step by step. We will create an Animal superclass and a Dog subclass, then trace what happens when we create a Dog object.
Creating a Dog That Extends Animal
- 1
Step 1 — Define the Superclass
We start with theAnimalclass. It has two private fields (nameandage), a constructor that initializes them, and aspeak()method.public class Animal { private String name; private int age; public Animal(String name, int age) { this.name = name; this.age = age; } public String speak() { return name + " makes a sound."; } public String getName() { return name; } public int getAge() { return age; } } - 2
Step 2 — Define the Subclass Using extends
Now we createDogusingextends Animal. It adds abreedfield and overridesspeak(). Notice that the constructor callssuper(name, age)as its very first line.public class Dog extends Animal { private String breed; public Dog(String name, int age, String breed) { super(name, age); // calls Animal's constructor this.breed = breed; } @Override public String speak() { return getName() + " barks! Woof!"; } public String getBreed() { return breed; } } - 3
Step 3 — Create a Dog Object
In ourmainmethod, we instantiate aDog.Dog myDog = new Dog("Rex", 3, "Labrador"); - 4
Step 4 — Trace the Constructor Chain
Whennew Dog("Rex", 3, "Labrador")runs, Java enters theDogconstructor and immediately encounterssuper("Rex", 3). This triggers theAnimalconstructor, which setsname = "Rex"andage = 3. Control returns to theDogconstructor, which then setsbreed = "Labrador". - 5
Step 5 — Call Methods and Observe Polymorphism
Now we can call methods on ourmyDogobject. BecauseDogoverridesspeak(), the overridden version runs.System.out.println(myDog.speak()); // Rex barks! Woof! System.out.println(myDog.getName()); // Rex (inherited) System.out.println(myDog.getAge()); // 3 (inherited) System.out.println(myDog.getBreed()); // Labrador (Dog-specific)
SECTION 7
Inheritance vs. Other Approaches
Inheritance is powerful, but it is not the only way to share code between classes. Understanding when to use inheritance versus other techniques is an important skill. The table below compares inheritance with two common alternatives.
| Approach | How It Works | Best Used When |
|---|---|---|
| Inheritance (extends) | Subclass automatically receives all non-private members of the superclass. Represents an "is-a" relationship. | The subclass truly is a specialized version of the superclass (e.g., Dog is-a Animal). |
| Composition (has-a) | A class stores a reference to another class as a field and delegates work to it. | The relationship is "has-a" (e.g., a Car has-an Engine). More flexible than inheritance. |
| Interfaces (implements) | A class promises to provide certain methods. Multiple interfaces can be implemented. | You need a common contract without shared implementation, or you need multiple types. |
Comparing code-reuse approaches in Java
WHEN TO CHOOSE INHERITANCE
SECTION 8
Connection to Advanced OOP Concepts
Inheritance with super is foundational, but it opens the door to more powerful concepts in Java. As you grow as a programmer, you will encounter ideas that build directly on what you've learned here.
| What You Know Now | What Comes Next |
|---|---|
| Extending a concrete class | Abstract classes — a superclass that cannot be instantiated and may contain abstract methods that subclasses must implement. |
| Overriding a single method | Polymorphism — using a superclass reference to call overridden methods, letting the JVM pick the correct version at runtime. |
| Single inheritance with extends | Interfaces — a class can implement multiple interfaces, gaining a form of multiple inheritance of type (not implementation). |
| Using super to call parent methods | The protected access modifier — grants subclasses direct access to fields/methods that outsiders cannot see, offering a middle ground between public and private. |
How inheritance connects to advanced Java concepts
Mastering extends and super now gives you a strong foundation. When you encounter abstract classes, polymorphism, and interfaces later, they will feel like natural extensions of what you already know — pun intended.
SECTION 9
Practice Problems
Question 1: CONCEPTUAL
super() call must be the first statement in a subclass constructor. What could go wrong if Java allowed other statements to come first?Show answer
super(), the subclass constructor might try to access inherited fields that have not been set yet, leading to unpredictable or incorrect behavior. By enforcing super() first, Java guarantees a safe, top-down initialization order from Object all the way down to the most specific subclass.Question 2: BASIC CALCULATION
Circle that extends Shape and adds a radius field. Use super to call the parent constructor.
public class Shape {
private String color;
public Shape(String color) {
this.color = color;
}
public String getColor() { return color; }
}Show answer
public class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color); // calls Shape(String)
this.radius = radius;
}
public double getRadius() { return radius; }
}
The key detail is that super(color) must appear as the first line of the Circle constructor. It delegates the initialization of the color field to Shape's constructor, and then Circle handles its own radius.Question 3: INTERMEDIATE
public class Vehicle {
private int speed;
public Vehicle(int speed) {
this.speed = speed;
}
}
public class Truck extends Vehicle {
private int payload;
public Truck(int speed, int payload) {
this.payload = payload;
super(speed);
}
}Show answer
super(speed) is on the second line of the Truck constructor instead of the first line. Java requires super() to be the very first statement. The fix is to swap the two lines:
public Truck(int speed, int payload) {
super(speed); // MUST be first
this.payload = payload;
}
With this fix, the Vehicle portion initializes speed before Truck sets payload.Question 4: APPLIED
Person superclass with name and age fields, and a Student subclass that adds gpa. Override a toString() method so that Student.toString() uses super.toString() and appends the GPA. Write the complete code for both classes.Show answer
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + " (age " + age + ")";
}
}
public class Student extends Person {
private double gpa;
public Student(String name, int age, double gpa) {
super(name, age);
this.gpa = gpa;
}
@Override
public String toString() {
return super.toString() + ", GPA: " + gpa;
}
}
Calling new Student("Alice", 16, 3.9).toString() returns "Alice (age 16), GPA: 3.9". The super.toString() call reuses the Person version so we don't repeat the name and age formatting logic.Question 5: CRITICAL THINKING
A has a method greet() that returns "Hello". Class B extends A and overrides greet() to return "Hi". Class C extends B and overrides greet() to return super.greet() + " there". What does new C().greet() return? Could class C directly access A's version of greet() using super.super.greet()? Explain why or why not.Show answer
new C().greet() returns "Hi there". Inside C.greet(), the call super.greet() refers to B.greet(), which returns "Hi". Java concatenates that with " there" to produce "Hi there". No, Java does not allow super.super.greet(). The language deliberately prevents skipping levels in the hierarchy because it would break encapsulation — class B chose to override greet() for a reason, and allowing grandchild classes to bypass that decision would undermine the design.SUMMARY
Lesson Summary
Summary
Inheritance lets a subclass automatically receive the fields and methods of a superclass by using the extends keyword. This supports the is-a relationship — a Dog is-an Animal, a Student is-a Person. The shared code lives in the superclass, and each subclass adds its own specialized fields and methods, keeping your codebase clean and maintainable.
The super keyword is your bridge back to the parent class. Use super() in constructors (always as the first statement) to initialize the inherited fields, and use super.methodName() in overridden methods when you want to build on the parent's behavior rather than replace it entirely. Remember: Java enforces single inheritance for classes, and the constructor chain always runs from the topmost superclass down to the most specific subclass.