Java - Method Overriding

Java Method Overriding – Complete Detailed Notes

Method Overriding in Java

Java Method Overriding is one of the most important concepts in Object-Oriented Programming (OOP). It enables runtime polymorphism, dynamic method dispatch, flexible code design, and implementation of real-world inheritance-based hierarchies. When a subclass provides its own implementation of a method already defined in the parent class, the feature is known as Method Overriding. This document explains every aspect of overriding with examples, outputs, features, rules, advantages, annotations, and real-time use cases. All explanations are expanded in detail for maximum clarity and enhanced reach.

Method Overriding in Java

Method Overriding in Java occurs when a subclass redeclares a method that exists with the same name, same return type, and same parameter list in its parent class. During execution, Java decides which method to run based on the object created, not based on the reference type. This is known as runtime polymorphism or dynamic binding. Overriding allows behavior modification in child classes without changing the parent class. It helps to implement features like abstraction, extensibility, plug-and-play design, and API customization. In real-world applications, overriding is used in frameworks, libraries, event handling systems, web applications, and layered architecture patterns.

Basic Example of Method Overriding


class Animal {
    void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    void sound() {
        System.out.println("Dog barks");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal a = new Dog();
        a.sound();
    }
}

Output:


Dog barks

Method Overriding is Used

Method Overriding is used to provide a specific implementation for a method already defined in the superclass. It allows subclasses to modify or extend inherited behavior. This is essential for designing flexible applications. Overriding supports dynamic polymorphism, which is one of the major pillars of Java OOP. It helps in creating customized class hierarchies, implementing design patterns like Template Pattern and Strategy Pattern, and enhancing reusability. It also ensures that Java applications can evolve over time without rewriting older classes. Method overriding also allows Java to implement real-world hierarchical models such as vehicle systems, employee structures, shape drawing programs, payments systems, etc.

Example: Customizing Superclass Behavior


class Vehicle {
    void start() {
        System.out.println("Vehicle is starting");
    }
}

class Car extends Vehicle {
    void start() {
        System.out.println("Car engine starts with key ignition");
    }
}

class Bike extends Vehicle {
    void start() {
        System.out.println("Bike starts with self-start button");
    }
}

public class Main {
    public static void main(String[] args) {
        Vehicle v;

        v = new Car();
        v.start();

        v = new Bike();
        v.start();
    }
}

Output:


Car engine starts with key ignition
Bike starts with self-start button

Rules for Method Overriding in Java

Java has strict rules for method overriding. These rules ensure consistency, compile-time validation, and predictable behavior. The method must have the same name and same parameter list; otherwise, it becomes method overloading instead of overriding. The return type must be the same or a covariant return type. The access modifier must be the same or more accessible. The overriding method cannot throw broader checked exceptions than the parent. Static, private, and final methods cannot be overridden. Constructors cannot be overridden. Additionally, the overriding method should follow object-oriented principles and business logic requirements.

Rule Demonstration


class Parent {
    protected Number display() {
        System.out.println("Parent display");
        return 10;
    }
}

class Child extends Parent {
    protected Integer display() {
        System.out.println("Child display");
        return 20;
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        p.display();
    }
}

Output:


Child display

Access Modifiers and Method Overriding

Access modifiers play a major role in method overriding. When overriding a method, the access level cannot be reduced. For example, if a method in the parent class is public, the child class cannot make it private or protected. Doing so results in a compilation error. However, the child may increase accessibility, such as changing protected to public. This allows flexibility while ensuring that overridden methods remain accessible where required. Proper use of access modifiers also maintains encapsulation and enhances class hierarchy design.

Access Modifier Example


class Parent {
    protected void show() {
        System.out.println("Parent show method");
    }
}

class Child extends Parent {
    public void show() {
        System.out.println("Child show method");
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        p.show();
    }
}

Output:


Child show method

Runtime Polymorphism (Dynamic Method Dispatch)

Runtime Polymorphism occurs when a method call is resolved at runtime rather than at compile time. This behavior is achieved through method overriding. When a superclass reference points to a subclass object, the overridden method in the subclass gets executed. Java uses dynamic binding to determine which method to call. This supports flexibility and extensibility in Java applications. It allows developers to write general code in the superclass and specialized behavior in subclasses. Runtime polymorphism makes Java powerful in frameworks, real-time applications, and enterprise systems.

Runtime Polymorphism Example


class Shape {
    void draw() {
        System.out.println("Drawing a shape");
    }
}

class Circle extends Shape {
    void draw() {
        System.out.println("Drawing a circle");
    }
}

class Square extends Shape {
    void draw() {
        System.out.println("Drawing a square");
    }
}

public class Main {
    public static void main(String[] args) {
        Shape s;

        s = new Circle();
        s.draw();

        s = new Square();
        s.draw();
    }
}

Output:


Drawing a circle
Drawing a square

The super Keyword in Method Overriding

The super keyword helps a subclass call the overridden method of the parent class. This is useful when the child wants to extend the parent functionality instead of completely replacing it. It allows hybrid behavior where the child executes its own code along with the parent class method. The super keyword also resolves naming conflicts and enhances code readability. It supports layered logic, logging, validation, and tracking operations in real-time applications.

super Keyword Example


class Parent {
    void message() {
        System.out.println("Message from Parent");
    }
}

class Child extends Parent {
    void message() {
        System.out.println("Message from Child");
        super.message();
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        c.message();
    }
}

Output:


Message from Child
Message from Parent

Final Methods Cannot Be Overridden

A final method in Java cannot be overridden. The final keyword ensures that the method remains unchanged in the subclass. This prevents accidental modification, ensures security, stabilizes behavior, and protects business logic. Final methods are commonly used in utility classes, security-related code, payment gateways, and sensitive logic implementations. Java enforces this rule at compile time, ensuring that developers do not override final methods by mistake. If attempted, the compiler immediately produces an error.

Example Demonstrating final


class Parent {
    final void display() {
        System.out.println("Final display method in Parent");
    }
}

class Child extends Parent {
    // Error: Cannot override final method
    // void display() {}
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Parent();
        p.display();
    }
}

Output:


Final display method in Parent

Static Methods and Method Hiding (Not Overriding)

Static methods cannot be overridden because static binding occurs at compile time. Instead, when a child class defines a static method with the same name as the parent, it is known as method hiding. The execution depends on the reference type, not the object type. This differs from overriding, where execution depends on the actual object. Method hiding has limited real-world usage but is important to understand as part of Java method resolution rules. Static methods belong to the class, not instances.

Method Hiding Example


class Parent {
    static void show() {
        System.out.println("Parent static show");
    }
}

class Child extends Parent {
    static void show() {
        System.out.println("Child static show");
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        p.show();
    }
}

Output:


Parent static show

Override Annotation

The @Override annotation is used to inform the compiler that the method is intended to override a parent class method. It improves readability and reduces errors. If the method signature does not match the parent method, the compiler will show an error. This helps prevent accidental overloading when overriding was intended. The annotation is widely used in all professional Java applications, frameworks, enterprise-level systems, and libraries. It also helps with documentation and tooling compatibility.

Override Example


class Parent {
    void greet() {
        System.out.println("Hello from Parent");
    }
}

class Child extends Parent {
    @Override
    void greet() {
        System.out.println("Hello from Child");
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        p.greet();
    }
}

Output:


Hello from Child

 Method Overriding

Method Overriding plays a major role in real-world Java programming. Frameworks like Spring, Hibernate, Android, and JavaFX rely heavily on overriding. Web applications use overriding to customize request handling, authentication, authorization, and data flow. GUI programs use overriding for event handling. Database applications override methods to implement data validation and transformation. Payment gateways override methods to apply different discount or tax rules. Overriding makes Java applications modular, reusable, scalable, and maintainable.

Example: Payment System


class Payment {
    void process() {
        System.out.println("Processing generic payment");
    }
}

class CreditCardPayment extends Payment {
    void process() {
        System.out.println("Processing credit card payment");
    }
}

class UPI extends Payment {
    void process() {
        System.out.println("Processing UPI payment");
    }
}

public class Main {
    public static void main(String[] args) {
        Payment p;

        p = new CreditCardPayment();
        p.process();

        p = new UPI();
        p.process();
    }
}

Output:


Processing credit card payment
Processing UPI payment


Method Overriding is one of the core features of Java OOP and enables runtime polymorphism, extensibility, and flexibility. It allows subclasses to modify inherited behavior and helps implement dynamic behavior in real-world systems. With rules, examples, and use cases, overriding becomes an essential skill for Java developers preparing for interviews, competitive programming, and enterprise-level application development. Mastering overriding enhances understanding of inheritance, polymorphism, abstraction, and code architecture patterns.

logo

Java

Beginner 5 Hours
Java Method Overriding – Complete Detailed Notes

Method Overriding in Java

Java Method Overriding is one of the most important concepts in Object-Oriented Programming (OOP). It enables runtime polymorphism, dynamic method dispatch, flexible code design, and implementation of real-world inheritance-based hierarchies. When a subclass provides its own implementation of a method already defined in the parent class, the feature is known as Method Overriding. This document explains every aspect of overriding with examples, outputs, features, rules, advantages, annotations, and real-time use cases. All explanations are expanded in detail for maximum clarity and enhanced reach.

Method Overriding in Java

Method Overriding in Java occurs when a subclass redeclares a method that exists with the same name, same return type, and same parameter list in its parent class. During execution, Java decides which method to run based on the object created, not based on the reference type. This is known as runtime polymorphism or dynamic binding. Overriding allows behavior modification in child classes without changing the parent class. It helps to implement features like abstraction, extensibility, plug-and-play design, and API customization. In real-world applications, overriding is used in frameworks, libraries, event handling systems, web applications, and layered architecture patterns.

Basic Example of Method Overriding

class Animal { void sound() { System.out.println("Animal makes a sound"); } } class Dog extends Animal { void sound() { System.out.println("Dog barks"); } } public class Main { public static void main(String[] args) { Animal a = new Dog(); a.sound(); } }

Output:

Dog barks

Method Overriding is Used

Method Overriding is used to provide a specific implementation for a method already defined in the superclass. It allows subclasses to modify or extend inherited behavior. This is essential for designing flexible applications. Overriding supports dynamic polymorphism, which is one of the major pillars of Java OOP. It helps in creating customized class hierarchies, implementing design patterns like Template Pattern and Strategy Pattern, and enhancing reusability. It also ensures that Java applications can evolve over time without rewriting older classes. Method overriding also allows Java to implement real-world hierarchical models such as vehicle systems, employee structures, shape drawing programs, payments systems, etc.

Example: Customizing Superclass Behavior

class Vehicle { void start() { System.out.println("Vehicle is starting"); } } class Car extends Vehicle { void start() { System.out.println("Car engine starts with key ignition"); } } class Bike extends Vehicle { void start() { System.out.println("Bike starts with self-start button"); } } public class Main { public static void main(String[] args) { Vehicle v; v = new Car(); v.start(); v = new Bike(); v.start(); } }

Output:

Car engine starts with key ignition Bike starts with self-start button

Rules for Method Overriding in Java

Java has strict rules for method overriding. These rules ensure consistency, compile-time validation, and predictable behavior. The method must have the same name and same parameter list; otherwise, it becomes method overloading instead of overriding. The return type must be the same or a covariant return type. The access modifier must be the same or more accessible. The overriding method cannot throw broader checked exceptions than the parent. Static, private, and final methods cannot be overridden. Constructors cannot be overridden. Additionally, the overriding method should follow object-oriented principles and business logic requirements.

Rule Demonstration

class Parent { protected Number display() { System.out.println("Parent display"); return 10; } } class Child extends Parent { protected Integer display() { System.out.println("Child display"); return 20; } } public class Main { public static void main(String[] args) { Parent p = new Child(); p.display(); } }

Output:

Child display

Access Modifiers and Method Overriding

Access modifiers play a major role in method overriding. When overriding a method, the access level cannot be reduced. For example, if a method in the parent class is public, the child class cannot make it private or protected. Doing so results in a compilation error. However, the child may increase accessibility, such as changing protected to public. This allows flexibility while ensuring that overridden methods remain accessible where required. Proper use of access modifiers also maintains encapsulation and enhances class hierarchy design.

Access Modifier Example

class Parent { protected void show() { System.out.println("Parent show method"); } } class Child extends Parent { public void show() { System.out.println("Child show method"); } } public class Main { public static void main(String[] args) { Parent p = new Child(); p.show(); } }

Output:

Child show method

Runtime Polymorphism (Dynamic Method Dispatch)

Runtime Polymorphism occurs when a method call is resolved at runtime rather than at compile time. This behavior is achieved through method overriding. When a superclass reference points to a subclass object, the overridden method in the subclass gets executed. Java uses dynamic binding to determine which method to call. This supports flexibility and extensibility in Java applications. It allows developers to write general code in the superclass and specialized behavior in subclasses. Runtime polymorphism makes Java powerful in frameworks, real-time applications, and enterprise systems.

Runtime Polymorphism Example

class Shape { void draw() { System.out.println("Drawing a shape"); } } class Circle extends Shape { void draw() { System.out.println("Drawing a circle"); } } class Square extends Shape { void draw() { System.out.println("Drawing a square"); } } public class Main { public static void main(String[] args) { Shape s; s = new Circle(); s.draw(); s = new Square(); s.draw(); } }

Output:

Drawing a circle Drawing a square

The super Keyword in Method Overriding

The super keyword helps a subclass call the overridden method of the parent class. This is useful when the child wants to extend the parent functionality instead of completely replacing it. It allows hybrid behavior where the child executes its own code along with the parent class method. The super keyword also resolves naming conflicts and enhances code readability. It supports layered logic, logging, validation, and tracking operations in real-time applications.

super Keyword Example

class Parent { void message() { System.out.println("Message from Parent"); } } class Child extends Parent { void message() { System.out.println("Message from Child"); super.message(); } } public class Main { public static void main(String[] args) { Child c = new Child(); c.message(); } }

Output:

Message from Child Message from Parent

Final Methods Cannot Be Overridden

A final method in Java cannot be overridden. The final keyword ensures that the method remains unchanged in the subclass. This prevents accidental modification, ensures security, stabilizes behavior, and protects business logic. Final methods are commonly used in utility classes, security-related code, payment gateways, and sensitive logic implementations. Java enforces this rule at compile time, ensuring that developers do not override final methods by mistake. If attempted, the compiler immediately produces an error.

Example Demonstrating final

class Parent { final void display() { System.out.println("Final display method in Parent"); } } class Child extends Parent { // Error: Cannot override final method // void display() {} } public class Main { public static void main(String[] args) { Parent p = new Parent(); p.display(); } }

Output:

Final display method in Parent

Static Methods and Method Hiding (Not Overriding)

Static methods cannot be overridden because static binding occurs at compile time. Instead, when a child class defines a static method with the same name as the parent, it is known as method hiding. The execution depends on the reference type, not the object type. This differs from overriding, where execution depends on the actual object. Method hiding has limited real-world usage but is important to understand as part of Java method resolution rules. Static methods belong to the class, not instances.

Method Hiding Example

class Parent { static void show() { System.out.println("Parent static show"); } } class Child extends Parent { static void show() { System.out.println("Child static show"); } } public class Main { public static void main(String[] args) { Parent p = new Child(); p.show(); } }

Output:

Parent static show

Override Annotation

The @Override annotation is used to inform the compiler that the method is intended to override a parent class method. It improves readability and reduces errors. If the method signature does not match the parent method, the compiler will show an error. This helps prevent accidental overloading when overriding was intended. The annotation is widely used in all professional Java applications, frameworks, enterprise-level systems, and libraries. It also helps with documentation and tooling compatibility.

Override Example

class Parent { void greet() { System.out.println("Hello from Parent"); } } class Child extends Parent { @Override void greet() { System.out.println("Hello from Child"); } } public class Main { public static void main(String[] args) { Parent p = new Child(); p.greet(); } }

Output:

Hello from Child

 Method Overriding

Method Overriding plays a major role in real-world Java programming. Frameworks like Spring, Hibernate, Android, and JavaFX rely heavily on overriding. Web applications use overriding to customize request handling, authentication, authorization, and data flow. GUI programs use overriding for event handling. Database applications override methods to implement data validation and transformation. Payment gateways override methods to apply different discount or tax rules. Overriding makes Java applications modular, reusable, scalable, and maintainable.

Example: Payment System

class Payment { void process() { System.out.println("Processing generic payment"); } } class CreditCardPayment extends Payment { void process() { System.out.println("Processing credit card payment"); } } class UPI extends Payment { void process() { System.out.println("Processing UPI payment"); } } public class Main { public static void main(String[] args) { Payment p; p = new CreditCardPayment(); p.process(); p = new UPI(); p.process(); } }

Output:

Processing credit card payment Processing UPI payment


Method Overriding is one of the core features of Java OOP and enables runtime polymorphism, extensibility, and flexibility. It allows subclasses to modify inherited behavior and helps implement dynamic behavior in real-world systems. With rules, examples, and use cases, overriding becomes an essential skill for Java developers preparing for interviews, competitive programming, and enterprise-level application development. Mastering overriding enhances understanding of inheritance, polymorphism, abstraction, and code architecture patterns.

Related Tutorials

Frequently Asked Questions for Java

Java is known for its key features such as object-oriented programming, platform independence, robust exception handling, multithreading capabilities, and automatic garbage collection.

The Java Development Kit (JDK) is a software development kit used to develop Java applications. The Java Runtime Environment (JRE) provides libraries and other resources to run Java applications, while the Java Virtual Machine (JVM) executes Java bytecode.

Java is a high-level, object-oriented programming language known for its platform independence. This means that Java programs can run on any device that has a Java Virtual Machine (JVM) installed, making it versatile across different operating systems.

Deadlock is a situation in multithreading where two or more threads are blocked forever, waiting for each other to release resources.

Functional programming in Java involves writing code using functions, immutability, and higher-order functions, often utilizing features introduced in Java 8.

A process is an independent program in execution, while a thread is a lightweight subprocess that shares resources with other threads within the same process.

The Comparable interface defines a natural ordering for objects, while the Comparator interface defines an external ordering.

The List interface allows duplicate elements and maintains the order of insertion, while the Set interface does not allow duplicates and does not guarantee any specific order.

String is immutable, meaning its value cannot be changed after creation. StringBuffer and StringBuilder are mutable, allowing modifications to their contents. The main difference between them is that StringBuffer is synchronized, making it thread-safe, while StringBuilder is not.

Checked exceptions are exceptions that must be either caught or declared in the method signature, while unchecked exceptions do not require explicit handling.

ArrayList is backed by a dynamic array, providing fast random access but slower insertions and deletions. LinkedList is backed by a doubly-linked list, offering faster insertions and deletions but slower random access.

Autoboxing is the automatic conversion between primitive types and their corresponding wrapper classes. For example, converting an int to Integer.

The 'synchronized' keyword in Java is used to control access to a method or block of code by multiple threads, ensuring that only one thread can execute it at a time.

Multithreading in Java allows concurrent execution of two or more threads, enabling efficient CPU utilization and improved application performance.

A HashMap is a collection class that implements the Map interface, storing key-value pairs. It allows null values and keys and provides constant-time performance for basic operations.

Java achieves platform independence by compiling source code into bytecode, which is executed by the JVM. This allows Java programs to run on any platform that has a compatible JVM.

The Serializable interface provides a default mechanism for serialization, while the Externalizable interface allows for custom serialization behavior.

The 'volatile' keyword in Java indicates that a variable's value will be modified by multiple threads, ensuring that the most up-to-date value is always visible.

Serialization is the process of converting an object into a byte stream, enabling it to be saved to a file or transmitted over a network.

The finalize() method is called by the garbage collector before an object is destroyed, allowing for cleanup operations.

The 'final' keyword in Java is used to define constants, prevent method overriding, and prevent inheritance of classes, ensuring that certain elements remain unchanged.

Garbage collection is the process by which the JVM automatically deletes objects that are no longer reachable, freeing up memory resources.

'throw' is used to explicitly throw an exception, while 'throws' is used in method declarations to specify that a method can throw one or more exceptions.

The 'super' keyword in Java refers to the immediate parent class and is used to access parent class methods, constructors, and variables.

The JVM is responsible for loading, verifying, and executing Java bytecode. It provides an abstraction between the compiled Java program and the underlying hardware, enabling platform independence.

line

Copyrights © 2024 letsupdateskills All rights reserved