Skip to content

Inheritance — extends, super, Method Overriding, and the Object Class

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Inheritance. We cover key concepts, practical examples, and best practices to help you master this topic.

Inheritance in Java allows a class to derive from another class, inheriting its fields and methods, using the extends keyword. Inheritance models "is-a" relationships — a Dog is an Animal, a SavingsAccount is a BankAccount — enabling code reuse and polymorphic behavior.

What You'll Learn

  • How to create subclasses with extends
  • Using super to call parent constructors and methods
  • Method overriding rules and the @Override annotation
  • The Object class and its key methods

Why It Matters

Inheritance is central to Java's type system. Collections, exceptions, and GUI components all use inheritance hierarchies. Misusing inheritance (creating deep hierarchies, breaking the Liskov substitution principle) leads to fragile, hard-to-maintain code.

Real-World Use

JDBC uses inheritance with different driver implementations (MySQLDriver, PostgreSQLDriver). Spring's @Controller classes extend base controller classes. Custom exceptions extend Exception.


Basic Inheritance

public class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }

    public void speak() {
        System.out.println(name + " makes a sound");
    }
}

public class Dog extends Animal {
    public Dog(String name) {
        super(name); // must call parent constructor
    }

    @Override
    public void speak() {
        System.out.println(name + " barks");
    }
}

Usage:

Animal a = new Animal("Generic");
a.speak(); // Generic makes a sound

Dog d = new Dog("Rex");
d.speak(); // Rex barks

Animal ref = new Dog("Buddy");
ref.speak(); // Buddy barks (polymorphism)

The super Keyword

Calling the Parent Constructor

The first statement in a constructor must be super(...) or this(...):

public Dog(String name) {
    super(name); // calls Animal(String) constructor
}

If you do not call super(), the compiler inserts super() automatically — but this only works if the parent has a no-arg constructor.

Calling a Parent Method

Use super.methodName() to call the overridden parent method:

@Override
public void speak() {
    super.speak(); // calls Animal.speak()
    System.out.println("...but more specifically, " + name + " barks");
}

Method Overriding

A subclass can provide a specific implementation of a method defined in the parent:

@Override
public void speak() {
    System.out.println(name + " barks");
}

Rules

  1. The method must have the same signature (name and parameter types)
  2. The return type must be the same or a covariant subtype (Java 5+)
  3. The access modifier cannot be more restrictive (public -> protected is invalid)
  4. The method cannot throw a broader checked exception than the parent
  5. The method must not be final or static

The @Override Annotation

Always use @Override. It makes the compiler check that you are actually overriding a method — catching typos like speek() instead of speak().

The Object Class

Every class in Java inherits from java.lang.Object. Key methods:

toString()

Returns a string representation. Default: ClassName@hashCode.

@Override
public String toString() {
    return "Dog{name='" + name + "'}";
}

equals(Object)

Default: reference equality (==). Override for value equality:

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null || getClass() != obj.getClass()) return false;
    Dog dog = (Dog) obj;
    return Objects.equals(name, dog.name);
}

hashCode()

Must be consistent with equals() — equal objects must have equal hash codes:

@Override
public int hashCode() {
    return Objects.hash(name);
}

finalize()

Deprecated since Java 9. Called by the garbage collector before reclaiming memory. Do not use it — use try-with-resources or Cleaner instead.

clone()

Creates a shallow copy. Must implement Cloneable (a marker interface) and handle CloneNotSupportedException.

The Liskov Substitution Principle

The Liskov Substitution Principle (LSP) states that a subclass should be substitutable for its parent class without altering the correctness of the program:

// VIOLATES LSP
class Rectangle {
    int width, height;
    void setWidth(int w) { width = w; }
    void setHeight(int h) { height = h; }
    int getArea() { return width * height; }
}

class Square extends Rectangle {
    @Override
    void setWidth(int w) {
        super.setWidth(w);
        super.setHeight(w);
    }

    @Override
    void setHeight(int h) {
        super.setWidth(h);
        super.setHeight(h);
    }
}

Rectangle r = new Square();
r.setWidth(5);
r.setHeight(10);
// Expected: 5 * 10 = 50, but Square makes it 10 * 10 = 100

Prefer Composition Over Inheritance when the "is-a" relationship is questionable.

Method Overloading vs Overriding

Aspect Overloading Overriding
Purpose Same method name, different parameters Subclass redefines parent behavior
Method signature Must differ (parameters) Must be identical
Return type Can differ Same or covariant
@Override Not used Required for clarity
Binding Compile-time (static) Runtime (dynamic)
static methods Can be overloaded Cannot be overridden (only hidden)

Common Mistakes

  1. Calling an overridable method from a constructor. The subclass's override runs before the subclass constructor body — may use uninitialized fields.
  2. Using super in a static context. super is tied to instances. Static methods cannot use super.
  3. Breaking the equals contract. If a.equals(b) is true, a.hashCode() == b.hashCode() must also be true.
  4. Creating deep inheritance hierarchies. More than 2-3 levels is usually a design smell. Prefer composition.
  5. Forgetting super() call when parent has no default constructor. You must explicitly call super(params) in the subclass constructor.

Practice Questions

1. What is the difference between method overloading and method overriding?
Overloading: same name, different parameters, compile-time binding. Overriding: same signature, runtime binding, changes behavior in subclasses.

2. Why should you always use @Override?
It enables compile-time checking. If the method does not actually override a parent method, the compiler reports an error.

3. What happens if a subclass does not call super()?
The compiler inserts super() automatically. If the parent has no no-arg constructor, this causes a compile error.

4. What methods does every Java object inherit?
toString(), equals(), hashCode(), clone(), finalize(), getClass(), notify(), notifyAll(), wait().

5. What is the Liskov Substitution Principle?
Objects of a subclass should be replaceable for objects of the parent class without changing program correctness. Violations occur when the subclass changes expected behavior.

Challenge Question:
Create a class hierarchy: Vehicle -> Car -> ElectricCar. Each should override toString() and add a specific field. Demonstrate polymorphism by storing ElectricCar in a Vehicle reference and calling methods. Add a method refuel() that throws in ElectricCar but works in Car.

FAQ

Why does Java not support multiple inheritance of classes?

Multiple inheritance of classes leads to the Diamond Problem — if two parent classes define the same method, which one does the child inherit? Java avoids this by supporting single class inheritance but multiple interface inheritance.

Can I prevent a class from being subclassed?

Yes. Declare the class as final. For example, public final class String { ... }. String is final because immutability requires preventing subclassing.

What is a covariant return type?

A covariant return type means an overriding method can return a subtype of the original return type. For example, if the parent returns Animal, the child can return Dog (a subclass of Animal).

Does Java support constructor inheritance?

No. Subclasses do not inherit constructors. They must define their own constructors and call super() to reuse parent construction logic.

What is the difference between `final` methods and non-final methods?

A final method cannot be overridden in subclasses. This is used for methods that should have invariant behavior (like template methods, security checks, or initialization logic).

Mini Project

Write a program VehicleHierarchy.java that:

  1. Defines an abstract Vehicle class with fields make, model, year and a method startEngine()
  2. Creates Car (adds numDoors) and Motorcycle (adds hasSidecar) subclasses
  3. Each subclass overrides startEngine(), toString(), and equals()
  4. Demonstrates polymorphic behavior with a Vehicle[] array
  5. Proves that Vehicle.class.isInstance(car) and car instanceof Vehicle both work
  6. Shows that an ElectricCar extends Car and uses super.startEngine() to reuse behavior

What's Next

Inheritance enables the most powerful feature of OOP: polymorphism. Lesson 14 dives into polymorphism — compile-time (overloading) and runtime (overriding), covariant return types, and how polymorphism enables frameworks to work with user-defined types.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro