Inheritance — extends, super, Method Overriding, and the Object Class
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
superto call parent constructors and methods - Method overriding rules and the
@Overrideannotation - The
Objectclass 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
- The method must have the same signature (name and parameter types)
- The return type must be the same or a covariant subtype (Java 5+)
- The access modifier cannot be more restrictive (
public->protectedis invalid) - The method cannot throw a broader checked exception than the parent
- The method must not be
finalorstatic
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
- Calling an overridable method from a constructor. The subclass's override runs before the subclass constructor body — may use uninitialized fields.
- Using
superin a static context.superis tied to instances. Static methods cannot usesuper. - Breaking the
equalscontract. Ifa.equals(b)is true,a.hashCode() == b.hashCode()must also be true. - Creating deep inheritance hierarchies. More than 2-3 levels is usually a design smell. Prefer composition.
- Forgetting
super()call when parent has no default constructor. You must explicitly callsuper(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
Mini Project
Write a program VehicleHierarchy.java that:
- Defines an abstract
Vehicleclass with fieldsmake,model,yearand a methodstartEngine() - Creates
Car(addsnumDoors) andMotorcycle(addshasSidecar) subclasses - Each subclass overrides
startEngine(),toString(), andequals() - Demonstrates polymorphic behavior with a
Vehicle[]array - Proves that
Vehicle.class.isInstance(car)andcar instanceof Vehicleboth work - Shows that an
ElectricCarextendsCarand usessuper.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