Extend behaviour with inheritance and achieve flexibility with polymorphism.
Published January 16, 2025
Inheritance lets a class reuse behaviour from a parent class. Polymorphism lets you write code that works with multiple types through a shared interface.
public class Animal {
protected String name;
public Animal(String name) { this.name = name; }
public String sound() { return "..."; }
}
public class Dog extends Animal {
public Dog(String name) { super(name); }
@Override
public String sound() { return "Woof"; }
}
public class Cat extends Animal {
public Cat(String name) { super(name); }
@Override
public String sound() { return "Meow"; }
}
List<Animal> animals = List.of(new Dog("Rex"), new Cat("Mimi"));
for (Animal a : animals) {
System.out.println(a.name + " says " + a.sound());
}
// Rex says Woof
// Mimi says Meow
The correct sound() method is selected at runtime based on the actual object type — this is called dynamic dispatch.
final, abstract, and sealedfinal class — cannot be extendedabstract class — cannot be instantiated; subclasses must implement abstract methodssealed class (Java 17+) — only permitted subclasses can extend itDistinguish method overriding (runtime polymorphism, @Override) from method overloading (compile-time, same name different parameters). Interviewers ask about this constantly.