java
- IntroductionInstallation and SetupJava Syntax and StructureRunning Your First ProgramJava VariablesData Types in JavaOperators in JavaJava Input and OutputControl StatementsLoops in JavaMethods in Java (Functions)Arrays in JavaString Handling in JavaOOPS Concept in JavaClasses and ObjectsConstructor and Constructor Overloadingthis KeywordStatic MembersInheritanceAggregation in JavaMethod OverloadingMethod Overridingsuper KeywordFinal Keyword with Class, Method, and VariablesAccess Modifiers in javaEncapsulation in Java Polymorphism in JavaAbstraction in JavaAbstract ClassesInterfaces in JavaDifference between Abstract Class and Interface Nested and Inner Classes Exception HandlingJava PackagesWrapper Classes and AutoboxingJava Collections FrameworkFile Handling in JavaMultithreadingBasics of Java Memory Management and Garbage CollectionJava JDBC
Java Method Overriding Tutorial with Real-Time Project Example
Last Updated on: 12th Nov 2025 15:08:29 PM
Welcome to this beginner-friendly tutorial on Method Overriding in Java! Method Overriding is a runtime polymorphism feature that allows a subclass to provide a specific implementation of a method that is already defined in its superclass.
This tutorial uses a real-world project-based example — a Banking System with Multiple Account Types — to show how method overriding is used in production software.
What is Method Overriding?
Method Overriding occurs when a subclass provides a new implementation of a method that is already defined in its superclass. The method in the subclass must have:
-
Same name
-
Same parameter list
-
Same return type (or covariant return type)
It is used to change or modify the behavior of an inherited method.
👉 It represents Runtime Polymorphism (also known as Dynamic Polymorphism).
Rules for Method Overriding
-
Method must be inherited (not private, final, or static)
-
Same name, parameters, and return type
-
Subclass method cannot reduce visibility (e.g., public → private)
-
Use @Override to catch errors
-
Can throw fewer or narrower exceptions
Example of Method Overriding
class Vehicle {
void run() {
System.out.println("The vehicle is running...");
}
}
class Bike extends Vehicle {
@Override
void run() {
System.out.println("The bike is running at 60 km/h.");
}
}
class Car extends Vehicle {
@Override
void run() {
System.out.println("The car is running at 100 km/h.");
}
}
public class VehicleTest {
public static void main(String[] args) {
Vehicle v1 = new Bike(); // runtime decision
Vehicle v2 = new Car();
v1.run(); // Calls Bike’s run()
v2.run(); // Calls Car’s run()
}
}
Output
The bike is running at 60 km/h.
The car is running at 100 km/h.
How Method Overriding Works Internally
-
The reference variable of the parent class points to a child class object.
-
At runtime, the JVM decides which method to call based on the actual object type, not the reference type.
-
This behavior is known as Dynamic Method Dispatch.
Important Points to Remember
| Keyword | Description |
|---|---|
@Override |
Ensures that you’re actually overriding a parent method (compiler checked). |
super |
Can be used inside the child class to call the parent class method. |
final |
If a method is final, it cannot be overridden. |
static |
Static methods are hidden, not overridden. |
Example: Using super Keyword
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
super.sound(); // Call parent method
System.out.println("Dog barks");
}
}
public class SuperExample {
public static void main(String[] args) {
Dog d = new Dog();
d.sound();
}
}
Output
Animal makes a sound
Dog barks
Real-Time Project: Banking System
Let’s build a Banking Application that supports:
-
Savings Account (with interest)
-
Current Account (with overdraft)
-
Fixed Deposit Account (locked funds)
We’ll use method overriding to calculate interest and withdrawal rules differently for each account type.
Step 1: Superclass – BankAccount
// BankAccount.java
public class BankAccount {
protected String accountNumber;
protected String holderName;
protected double balance;
public BankAccount(String accountNumber, String holderName, double initialBalance) {
this.accountNumber = accountNumber;
this.holderName = holderName;
this.balance = initialBalance;
}
// Common method - can be overridden
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("Deposited: ₹" + amount + " | New Balance: ₹" + balance);
} else {
System.out.println("Invalid deposit amount.");
}
}
// Method to be overridden
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("Withdrawn: ₹" + amount + " | Remaining: ₹" + balance);
} else {
System.out.println("Insufficient funds or invalid amount.");
}
}
// Method to be overridden
public void calculateInterest() {
System.out.println("No interest applicable for basic account.");
}
// Display account info
public void displayInfo() {
System.out.println("Account: " + accountNumber);
System.out.println("Holder: " + holderName);
System.out.println("Balance: ₹" + balance);
}
}
Step 2: Subclasses with Overridden Methods
1. SavingsAccount.java
public class SavingsAccount extends BankAccount {
private double interestRate = 4.0; // 4% per year
public SavingsAccount(String accountNumber, String holderName, double initialBalance) {
super(accountNumber, holderName, initialBalance);
}
@Override
public void withdraw(double amount) {
double minBalance = 1000;
if (amount > 0 && (balance - amount) >= minBalance) {
balance -= amount;
System.out.println("[Savings] Withdrawn: ₹" + amount + " | Balance: ₹" + balance + " (Min ₹1000 maintained)");
} else {
System.out.println("[Savings] Withdrawal failed: Minimum balance ₹1000 required.");
}
}
@Override
public void calculateInterest() {
double interest = balance * (interestRate / 100);
balance += interest;
System.out.println("[Savings] Interest added: ₹" + interest + " | New Balance: ₹" + balance);
}
}
2. CurrentAccount.java
public class CurrentAccount extends BankAccount {
private double overdraftLimit = 5000;
public CurrentAccount(String accountNumber, String holderName, double initialBalance) {
super(accountNumber, holderName, initialBalance);
}
@Override
public void withdraw(double amount) {
double maxWithdraw = balance + overdraftLimit;
if (amount > 0 && amount <= maxWithdraw) {
balance -= amount;
System.out.println("[Current] Withdrawn: ₹" + amount + " | Balance: ₹" + balance + " (Overdraft used if negative)");
} else {
System.out.println("[Current] Withdrawal exceeds overdraft limit of ₹5000.");
}
}
@Override
public void calculateInterest() {
System.out.println("[Current] No interest on current accounts.");
}
}
3. FixedDepositAccount.java
public class FixedDepositAccount extends BankAccount {
private double interestRate = 7.5;
private boolean isMatured = false;
public FixedDepositAccount(String accountNumber, String holderName, double initialBalance) {
super(accountNumber, holderName, initialBalance);
}
@Override
public void withdraw(double amount) {
if (isMatured) {
super.withdraw(amount); // Use parent's logic
} else {
System.out.println("[FD] Withdrawal not allowed: FD not matured yet!");
}
}
@Override
public void calculateInterest() {
double interest = balance * (interestRate / 100);
balance += interest;
System.out.println("[FD] High interest added: ₹" + interest + " | Total: ₹" + balance);
}
public void matureFD() {
isMatured = true;
System.out.println("[FD] Fixed Deposit matured. Withdrawal now allowed.");
}
}
Step 3: Main App – BankingSystem
// BankingSystem.java
import java.util.ArrayList;
import java.util.List;
public class BankingSystem {
public static void main(String[] args) {
// Polymorphic List
List<BankAccount> accounts = new ArrayList<>();
accounts.add(new SavingsAccount("SAV001", "Amit Sharma", 5000));
accounts.add(new CurrentAccount("CUR001", "Priya Verma", 10000));
accounts.add(new FixedDepositAccount("FD001", "Raj Patel", 50000));
System.out.println("BANKING SYSTEM - MONTHLY PROCESSING\n");
// Runtime Polymorphism in action
for (BankAccount account : accounts) {
account.displayInfo();
account.deposit(1000);
account.withdraw(2000);
account.calculateInterest();
System.out.println("-".repeat(50));
}
// Special case: Mature FD
FixedDepositAccount fd = (FixedDepositAccount) accounts.get(2);
fd.matureFD();
fd.withdraw(10000);
}
}
Output
BANKING SYSTEM - MONTHLY PROCESSING
Account: SAV001
Holder: Amit Sharma
Balance: ₹5000.0
Deposited: ₹1000.0 | New Balance: ₹6000.0
[Savings] Withdrawn: ₹2000.0 | Balance: ₹4000.0 (Min ₹1000 maintained)
[Savings] Interest added: ₹160.0 | New Balance: ₹4160.0
--------------------------------------------------
Account: CUR001
Holder: Priya Verma
Balance: ₹10000.0
Deposited: ₹1000.0 | New Balance: ₹11000.0
[Current] Withdrawn: ₹2000.0 | Balance: ₹9000.0 (Overdraft used if negative)
[Current] No interest on current accounts.
--------------------------------------------------
Account: FD001
Holder: Raj Patel
Balance: ₹50000.0
Deposited: ₹1000.0 | New Balance: ₹51000.0
[FD] Withdrawal not allowed: FD not matured yet!
[FD] High interest added: ₹3825.0 | Total: ₹54825.0
--------------------------------------------------
[FD] Fixed Deposit matured. Withdrawal now allowed.
Withdrawn: ₹10000.0 | Remaining: ₹44825.0
Why Use Method Overriding? (Real Project Benefits)
|
Feature |
Benefit |
|---|---|
|
withdraw() |
Different rules per account type |
|
calculateInterest() |
Unique logic for each account |
|
Runtime Decision |
Correct method called based on actual object |
|
Extensible |
Add StudentAccount, NRIAccount easily |
How Java Chooses the Right Method?
|
Object |
Method Called |
|---|---|
|
new SavingsAccount() |
SavingsAccount.withdraw() |
|
new CurrentAccount() |
CurrentAccount.withdraw() |
JVM decides at runtime using vtable (virtual method table).
Real-Time Use Cases
|
System |
Overridden Method |
|---|---|
|
Payment Gateway |
processPayment() in CreditCard, UPI, Wallet |
|
Notification System |
send() in Email, SMS, Push |
|
Report Generator |
generate() in PDFReport, ExcelReport |
|
Vehicle System |
startEngine() in ElectricCar, PetrolCar |
Common Mistakes
// NOT overriding
public void deposit(int amount) { } // Different parameter type
// Valid override
@Override
public void deposit(double amount) { } // Same signature
// Cannot override final method
class A { final void lock() {} }
class B extends A { void lock() {} } // ERROR
Summary
-
Method Overriding allows a subclass to modify behavior of its superclass method.
-
It enables runtime polymorphism, improving flexibility and reusability.
-
Always use the
@Overrideannotation for clarity and safety. -
It’s one of the most important OOP concepts in Java.
Project Tip:
In your Banking App, override:
-
applyCharges() for different account types
-
sendAlert() for SMS/email preferences
-
generateStatement() for PDF/HTML
You now master Method Overriding in Java with a real-world, scalable banking system!
Keep building — happy coding! ![]()
.png)