While Method Overloading allows the same method name within one class, Method Overriding allows a child class to provide a completely different implementation for a method that is already defined in its parent class. The decision of which version to call is made at runtime, not compile time.
When a subclass has a method with the exact same signature (name, parameters, and return type) as a method in its superclass, the subclass method overrides the parent method.
class Animal {
void speak() {
System.out.println("The animal makes a sound");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("The dog barks: Woof!");
}
}
class Cat extends Animal {
@Override
void speak() {
System.out.println("The cat meows: Meow!");
}
}
public, the child cannot make it private.static, final, and private methods cannot be overridden.The truly powerful aspect of overriding is Dynamic Method Dispatch. A parent class reference variable can hold an object of any child class. The JVM decides which version of the overridden method to call at runtime based on the actual type of the object, not the declared type of the reference variable.
Animal myAnimal; // Declared type is Animal
myAnimal = new Dog();
myAnimal.speak(); // Output: "The dog barks: Woof!"
myAnimal = new Cat();
myAnimal.speak(); // Output: "The cat meows: Meow!"
The variable myAnimal is declared as type Animal, but at runtime, the JVM inspects the actual object it points to and calls the correct overridden method. This decision cannot be made at compile time because the compiler might not know which child object will be assigned to myAnimal (it could depend on user input, a database query, or a random number).
Dynamic dispatch enables incredibly flexible and extensible code:
class AnimalShelter {
void makeAllSpeak(Animal[] animals) {
for (Animal a : animals) {
a.speak(); // Correct version called automatically!
}
}
}
Animal[] shelter = { new Dog(), new Cat(), new Dog() };
// Output:
// The dog barks: Woof!
// The cat meows: Meow!
// The dog barks: Woof!
The makeAllSpeak method doesn't know (or care) about Dog or Cat. It only knows about Animal. If you later add a Parrot class, you don't need to change makeAllSpeak at all. The new Parrot.speak() will be automatically called via dynamic dispatch.
| Feature | Overloading | Overriding |
|---|---|---|
| Scope | Within the same class | Between parent and child class |
| Signature | Same name, different parameters | Same name, same parameters |
| Binding | Compile-Time (Static) | Run-Time (Dynamic) |
| Purpose | Convenience (multiple ways to call) | Specialization (custom behavior) |