- 1Java OOP Introduction
- 2Java Class
- 3Java Class Constructor
- 4Java Class Objects
- 5Java Access Modifiers
- 6Java Static Variables in Classes
- 7Java Static Methods Explained
- 8Java Static Blocks
- 9Java final Variables
- 10Java final Methods
- 11Java final class
- 12Inheritance in Java
- 13Java Method Overriding
- 14Java Abstraction in OOP
- 15Interfaces in Java
- 16Polymorphism in Java
- 17Encapsulation in Java
- 18Java Nested Classes
- 19Java Nested Static Class
- 20Java Anonymous Class
- 21Java Singleton Class
- 22Java Enums
- 23Reflection in Java
Java Method Overriding
Runtime Polymorphism
Method Overriding allows a subclass to provide a specific implementation of a method that is already defined in its superclass. This enables dynamic behavior and helps in achieving runtime polymorphism.
Why Do We Override Methods?
Imagine you’re building a system where the parent class defines a generic action — like makeSound()
— but the child classes need to provide their own unique version of this behavior. That’s where method overriding shines. It lets subclasses tailor inherited behavior to fit their specific needs.
Rules for Method Overriding
- The method must have the same name, return type, and parameter list as the method in the parent class.
- The method in the child class must be accessible and not more restrictive than the one in the parent class.
- Only inherited methods can be overridden (not constructors or private methods).
@Override
annotation is optional but highly recommended for clarity and error detection.
Simple Example of Method Overriding
class Animal {
void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog();
myDog.makeSound(); // Which method runs?
}
}
Dog barks
Explanation
Even though myDog
is declared as type Animal
, the makeSound()
method of the Dog
class is invoked. This is because of runtime polymorphism — Java determines the method to call based on the actual object type at runtime, not the reference type.
Using @Override Annotation
The @Override
annotation tells the compiler that we intend to override a method. If the method signature doesn't exactly match, the compiler throws an error. This helps catch typos and logic bugs early.
class Parent {
void show() {
System.out.println("Parent show");
}
}
class Child extends Parent {
@Override
void show() {
System.out.println("Child show");
}
}
Calling Superclass Method Inside Overridden Method
Sometimes, we want to extend the functionality of the superclass method rather than completely replacing it. We can do that using super.methodName()
.
class Vehicle {
void start() {
System.out.println("Vehicle is starting");
}
}
class Car extends Vehicle {
@Override
void start() {
super.start(); // Call the superclass method
System.out.println("Car is starting");
}
}
public class Main {
public static void main(String[] args) {
Vehicle v = new Car();
v.start();
}
}
Vehicle is starting
Car is starting
Final Methods Cannot Be Overridden
If a method is declared with the final
keyword in the parent class, it cannot be overridden by any subclass. Attempting to do so will result in a compile-time error.
class Shape {
final void draw() {
System.out.println("Drawing Shape");
}
}
class Circle extends Shape {
// Compilation error
void draw() {
System.out.println("Drawing Circle");
}
}
Overriding vs Overloading
Feature | Method Overriding | Method Overloading |
---|---|---|
Definition | Redefining a method of the parent class in the child class | Defining multiple methods in the same class with the same name but different parameters |
Inheritance | Requires inheritance | No inheritance required |
Runtime/Compile-time | Runtime polymorphism | Compile-time polymorphism |
Real-World Use Case
Suppose you're creating a payment system. You define a generic processPayment()
method in the superclass. Credit cards, UPI, and net banking can each override this method to implement their specific logic.
class Payment {
void processPayment() {
System.out.println("Processing generic payment");
}
}
class CreditCardPayment extends Payment {
@Override
void processPayment() {
System.out.println("Processing credit card payment");
}
}
Key Takeaways
- Overriding allows dynamic method resolution during runtime.
- The method signature must match exactly with the superclass method.
- Use
@Override
to avoid unintentional mistakes. - Polymorphism is made possible by method overriding.
QUIZ
Question 1:What principle does method overriding primarily support in Java?
Question 2:The method signature in a subclass can differ from the one in the superclass while still overriding it.
Question 3:What happens when you run the following code?
class Animal {
void speak() {
System.out.println("Animal speaks");
}
}
class Cat extends Animal {
void speak() {
System.out.println("Cat meows");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Cat();
a.speak();
}
}
class Animal {
void speak() {
System.out.println("Animal speaks");
}
}
class Cat extends Animal {
void speak() {
System.out.println("Cat meows");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Cat();
a.speak();
}
}
Question 4:Which of the following are true about method overriding?
Question 5:Overriding a method with a more restrictive access modifier than the superclass method will result in a compilation error.
Question 6:What is the output of the following program?
class Vehicle {
void start() {
System.out.println("Vehicle is starting");
}
}
class Car extends Vehicle {
@Override
void start() {
super.start();
System.out.println("Car is starting");
}
}
public class Main {
public static void main(String[] args) {
Vehicle v = new Car();
v.start();
}
}
class Vehicle {
void start() {
System.out.println("Vehicle is starting");
}
}
class Car extends Vehicle {
@Override
void start() {
super.start();
System.out.println("Car is starting");
}
}
public class Main {
public static void main(String[] args) {
Vehicle v = new Car();
v.start();
}
}