By the end of this module, you will be able to:
extends keyword to create a subclasssuper to call a parent constructor and parent methods@Override annotation correctly and explain its purposeObject class as the root of all Java classesInheritance is a mechanism that lets one class acquire the fields and methods of another. It models an "is-a" relationship — if you can truthfully say "A is a B", inheritance is likely the right tool.
Some examples:
Dog is a Animal ✅SavingsAccount is a BankAccount ✅Car is a Vehicle ✅Car is a Engine ❌ — a car has an engine, not is oneThe last example is important. "Has-a" relationships should use composition (a field), not inheritance. Choosing the wrong one is one of the most common OOP design mistakes.
🔍 You saw inheritance in JavaScript with
class Dog extends Animal. Java works the same way conceptually — the syntax is nearly identical, but Java enforces stricter rules around types and access that make the structure more explicit.
Without inheritance, you'd have to repeat shared fields and methods in every class:
// Without inheritance — duplicated code
public class Dog {
String name;
int age;
public void eat() { System.out.println("Eating..."); }
public void bark() { System.out.println("Woof!"); }
}
public class Cat {
String name; // duplicated
int age; // duplicated
public void eat() { System.out.println("Eating..."); } // duplicated
public void meow() { System.out.println("Meow!"); }
}
With inheritance, shared code lives in one place:
// With inheritance — shared code defined once
public class Animal {
String name;
int age;
public void eat() { System.out.println("Eating..."); }
}
public class Dog extends Animal {
public void bark() { System.out.println("Woof!"); }
}
public class Cat extends Animal {
public void meow() { System.out.println("Meow!"); }
}
extends KeywordUse extends to declare that one class inherits from another:
public class SubClass extends SuperClass {
// SubClass now has everything SuperClass has, plus its own additions
}
The class being extended is called the superclass (parent). The class doing the extending is called the subclass (child).
public class Animal { // superclass
String name;
int age;
public void eat() {
System.out.println(name + " is eating.");
}
}
public class Dog extends Animal { // subclass
public void bark() {
System.out.println(name + " says: Woof!"); // name inherited from Animal
}
}
Dog dog = new Dog();
dog.name = "Rex"; // inherited field
dog.age = 3; // inherited field
dog.eat(); // inherited method → Rex is eating.
dog.bark(); // own method → Rex says: Woof!
<aside> 💭
Java supports single inheritance only — a class can extend at most one other class. This is different from some other languages (like C++) that allow multiple inheritance. Java addresses this limitation with interfaces, which you'll cover in the next chapter.
</aside>
Not everything from the superclass is automatically available in the subclass:
| Member | Inherited? | Notes |
|---|---|---|
public fields |
✅ Yes | Directly accessible |
protected fields |
✅ Yes | Accessible in subclass |
public methods |
✅ Yes | Can be called and overridden |
protected methods |
✅ Yes | Can be called and overridden |
private fields |
❌ No | Exist in memory but not directly accessible — use getters |
private methods |
❌ No | Not accessible in subclass |
| Constructors | ❌ No | Must be defined separately in each class |
public class Animal {
private String name; // private — not directly accessible in subclass
protected int age; // protected — accessible in subclass
public String getName() { return name; } // public getter — inherited ✅
public void setName(String name) { this.name = name; }
}
public class Dog extends Animal {
public void describe() {
// System.out.println(name); // ❌ private — not accessible
System.out.println(getName()); // ✅ use the inherited getter
System.out.println(age); // ✅ protected — directly accessible
}
}
<aside> 💭
This is why encapsulation and inheritance work together — making fields private and exposing them through public/protected methods keeps the superclass in control of its own data even as subclasses extend it.
</aside>
super Keyword — Calling the Parent ConstructorSince constructors are not inherited, each subclass must define its own. But often a subclass constructor needs to initialise the fields defined in the superclass. The super() call delegates this to the parent constructor.
public class Animal {
private String name;
private int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
public class Dog extends Animal {
private String breed;
public Dog(String name, int age, String breed) {
super(name, age); // calls Animal(String, int) — must be first line
this.breed = breed;
}
public String getBreed() { return breed; }
}
Dog dog = new Dog("Rex", 3, "Labrador");
System.out.println(dog.getName()); // Rex
System.out.println(dog.getAge()); // 3
System.out.println(dog.getBreed()); // Labrador
Rules for super():
super() explicitly, Java inserts a no-arg super() automatically — but only if the superclass has a no-arg constructor. If it doesn't, you'll get a compile error.<aside> 💭
In JavaScript, you also call super() at the top of a subclass constructor before using this. Java enforces the same rule — the difference is Java will refuse to compile if you forget.
</aside>
super for Calling Parent Methodssuper isn't only for constructors. You can also use it to call a method from the superclass when you've overridden it in the subclass and still want to use the parent's version:
public class Animal {
public void describe() {
System.out.println("I am an animal.");
}
}
public class Dog extends Animal {
@Override
public void describe() {
super.describe(); // calls Animal's describe()
System.out.println("I am a dog."); // adds Dog-specific behaviour
}
}
Dog dog = new Dog();
dog.describe();
// I am an animal.
// I am a dog.
This is useful when you want to extend the parent's behaviour rather than completely replace it.
Overriding means redefining a method from the superclass in the subclass with the same signature (same name, same parameters, same return type) but different behaviour.
public class Animal {
public String makeSound() {
return "Some generic sound";
}
}
public class Dog extends Animal {
@Override
public String makeSound() {
return "Woof!";
}
}
public class Cat extends Animal {
@Override
public String makeSound() {
return "Meow!";
}
}
Animal a = new Animal();
Animal d = new Dog();
Animal c = new Cat();
System.out.println(a.makeSound()); // Some generic sound
System.out.println(d.makeSound()); // Woof!
System.out.println(c.makeSound()); // Meow!
Notice that d and c are declared as type Animal, but they run the Dog and Cat versions of makeSound(). This is polymorphism — the ability to treat different objects through a common type, and have each respond in its own way. You'll go deeper into this in the next module.
Rules for overriding:
private method — it's not visible to the subclassfinal method — final on a method means "no subclass may override this"public with private)@Override Annotation@Override is placed above a method to tell the compiler: "I intend this method to override one from the superclass."
@Override
public String makeSound() {
return "Woof!";
}
It is not required — the override happens with or without it. But it gives you two important benefits:
@Override, Java would silently create a new method instead of overriding, which is a very hard bug to spot.// Without @Override — typo goes undetected
public String makesound() { // lowercase 's' — not an override, a new method
return "Woof!";
}
// With @Override — compiler catches the typo immediately
@Override
public String makesound() { // ❌ Compile error: method does not override
return "Woof!";
}
<aside> 💡
</aside>