Java Method Overriding: Subclasses Override Parent Class Methods to Implement Polymorphism Fundamentals

Method Overriding: How Subclasses “Modify” Parent Class Methods?

In Java, when creating a subclass to inherit from a parent class, there are times when you need to “personally customize” the parent class’s methods—this is method overriding. Simply put, method overriding means the subclass rewrites the specific implementation of a method while keeping the method declaration (name, parameters, etc.) unchanged, based on its own needs.

Why is Method Overriding Needed?

Imagine: The parent class defines a generic method “animal eats”. Subclasses “Dog” and “Cat” are both animals, but their eating behaviors differ (dogs eat bones, cats eat fish). Directly using the parent class method clearly doesn’t meet the subclass’s needs. Method overriding allows subclasses to flexibly modify the content of this method while maintaining the consistency of the method declaration.

Rules for Method Overriding (Must-Have Conditions)

To correctly implement method overriding, the following rules must be met (none can be missing):

  1. Method Name and Parameter List Must Be Exactly the Same
    The subclass method’s name, parameter quantity, type, and order must be consistent with the parent class method. For example: If the parent class has eat(), the subclass override must also be eat().

  2. Covariant Return Type
    The return type of the subclass method can be the parent class method’s return type itself or a “subclass type” of it (e.g., parent returns Object, subclass returns String; parent returns Animal, subclass returns Dog).

  3. Access Permissions Cannot Be Strictly Restricted
    The access permission of the subclass method must be greater than or equal to that of the parent class method. For example: If the parent class method is public, the subclass can be public or protected; if the parent class is protected, the subclass cannot be written as private (narrowing permissions will cause compilation errors).

  4. Exception Handling Must Be More Lenient
    The exceptions thrown by the subclass method must be “subtypes” of the exceptions thrown by the parent class method or fewer exceptions (cannot throw more or broader exceptions than the parent class).

Example: Overriding “Eating” for Animals

Let’s use code to intuitively understand method overriding:

// Parent class: Animal
class Animal {
    // Parent class method: eat (general implementation)
    public void eat() {
        System.out.println("Animal eats");
    }
}

// Subclass: Dog (inherits from Animal)
class Dog extends Animal {
    // Override the parent class's eat() method
    @Override
    public void eat() {
        System.out.println("Dog eats bones!");
    }
}

// Subclass: Cat (inherits from Animal)
class Cat extends Animal {
    // Override the parent class's eat() method
    @Override
    public void eat() {
        System.out.println("Cat eats fish!");
    }
}

// Test class
public class TestOverride {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Cat cat = new Cat();

        dog.eat(); // Output: Dog eats bones!
        cat.eat(); // Output: Cat eats fish!
    }
}

Key Point: The @Override annotation is optional, but adding it helps the compiler check if the overriding rules are truly satisfied (e.g., a misspelled method name will trigger an error). It is recommended to form the habit of using it.

Relationship Between Method Overriding and Polymorphism

Method overriding is a core foundation of polymorphism, especially for “runtime polymorphism” (dynamic binding). When a parent class reference points to a subclass object, the overridden method will automatically execute the subclass’s implementation.

// Parent class reference pointing to a subclass object
Animal a = new Dog(); 
a.eat(); // Output: Dog eats bones! (executes the subclass's overridden method)

a = new Cat();
a.eat(); // Output: Cat eats fish! (executes the subclass's overridden method)

Here, a is of type Animal but actually points to a Dog or Cat object. When calling eat(), the method implementation corresponding to the object’s actual type (subclass) is executed—this is the manifestation of “polymorphism”.

Method Overriding vs Method Overloading (Don’t Confuse!)

Beginners often confuse method overriding with method overloading. The differences between the two are as follows:

Comparison Item Method Overriding (Override) Method Overloading (Overload)
Occurrence Scenario Subclass modifies the parent class’s method Same class: method names are the same, parameters are different
Core Goal Extend/modify parent class behavior to achieve polymorphism Different parameter versions of the same function (different parameter lists)
Rules Names, parameter lists, and return values must be consistent Same name, different parameter lists (quantity/type/order)

Summary

Method overriding is an important mechanism in Java for achieving code reuse and extension. It allows subclasses to both inherit the parent class’s “framework” and modify the “details” according to their own needs. Meanwhile, method overriding is the core foundation of polymorphism—by using a parent class reference to point to a subclass object, the program can automatically select the correct method implementation at runtime, significantly enhancing the flexibility and scalability of the code.

Practice: Try defining a Bird class that inherits from Animal, override the eat() method to output “Bird eats insects”, and test it using polymorphism!

Xiaoye