By WebNest Studio Engineering Team
Java - Core Tutorial
Method Overriding
Method overriding occurs when a subclass provides its own implementation of a method that is already defined, with the exact same signature, in its superclass. Unlike overloading, which the compiler resolves at compile time, overriding is resolved at runtime based on the actual type of the object — this is the basis of runtime polymorphism.
The subclass keeps the parent's method name and parameters, so any code written against the parent type keeps working, but the body that runs is the subclass's own. That one idea lets a program grow new behaviour by adding classes instead of editing the code that calls them.
Why it matters
Overriding is how a subclass changes behaviour it inherited. A small mistake — a misspelt name or a slightly different parameter type — silently creates a new, unrelated method instead of an override, and the parent's version keeps running. The @Override annotation turns that silent bug into a compile error, which is why experienced developers always write it.
Watch: Method Overriding in Java – Complete Guide with Examples | Core Java Tutorial
Why subclasses override methods and every rule an override must follow: matching signatures, covariant return types, access modifiers, checked vs unchecked exceptions, the @Override annotation, why static, final and private methods cannot be overridden, calling the parent with super, dynamic method dispatch, overloading vs overriding, and interview questions.
Why We Need Method Overriding
Inheritance gives a subclass the parent's methods, but a general version is often not right for a specific type. A Payment class can define pay(int amount), yet a card payment adds a fee and a UPI payment does not. Overriding lets each subclass supply the correct behaviour while the rest of the program keeps calling the same pay method on a Payment reference.
- Specialised behaviour: each subclass does the job its own way (
CardPayment,UpiPayment,WalletPayment). - Code written once: a loop over
Paymentobjects never needs anifchain checking which kind of payment it holds. - Easy extension: adding a new payment type means adding a class, not editing every place that takes payments.
- Everyday Java: you override
toString(),equals()andhashCode()fromObjectall the time, and frameworks call your overridden methods (for examplerun()in aThread).
Rules an Override Must Follow
For a subclass method to count as a valid override, several strict rules apply, and the compiler enforces every one of them.
- The method name and parameter list (the signature) must be identical to the superclass method.
- The return type must be the same, or a covariant return type — a subtype of the original return type — which Java has allowed since version 5.
- The access modifier in the subclass cannot be more restrictive than in the superclass (you cannot override a
publicmethod with aprotectedone, for example), though it may be less restrictive. - The overriding method cannot throw new or broader checked exceptions than the overridden method declares.
- A method marked
final,static, orprivatein the superclass cannot be overridden at all.
The @Override Annotation
@Override is an optional but strongly recommended annotation placed above an overriding method. It does not change runtime behavior, but it instructs the compiler to verify that the method actually overrides a superclass method — if you misspell the method name or get the parameter list wrong, the compiler raises an error immediately instead of silently creating an unrelated overloaded method.
Without the annotation, a method named tostring() in your class compiles happily, and System.out.println(obj) still prints the default ClassName@1b6d3586 text, because Object.toString() was never overridden. With @Override on it, javac stops with method does not override or implement a method from a supertype. Put it on every override; it costs nothing.
Covariant Return Types
An override normally returns the same type as the parent method, but Java also accepts a subtype of that return type. If Shape.createCopy() returns Shape, then Circle.createCopy() may return Circle, because every Circle is a Shape and callers using the parent type still get what they expect. The benefit shows up for callers who hold a Circle reference: they get a Circle back with no cast. Covariance works only for reference types. A parent method returning int cannot be overridden with one returning long or Integer.
Exception Rules: Checked vs Unchecked
Code that calls a method through the parent type only prepares for the checked exceptions the parent method declares. An override is therefore not allowed to surprise that code with a new or broader checked exception.
- An override may throw the same checked exception, a narrower one (a subclass, such as
FileNotFoundExceptioninstead ofIOException), or none at all. - An override may not throw a new or broader checked exception. If the parent declares
IOException, the child cannot declareException; if the parent declares nothing, the child cannot declare any checked exception. - Unchecked exceptions (
RuntimeExceptionand its subclasses such asIllegalStateException) are not restricted. An override may throw them whatever the parent declares. - The rule is about checked exceptions only, and javac reports a violation as
overridden method does not throw ....
Can static, final and private Methods Be Overridden?
No, and each one fails for a different reason, which is why interviewers like asking about all three together.
- final: the parent has locked the method. Declaring the same method in a subclass is a compile error:
overridden method is final. - private: the method is not inherited, so the subclass cannot see it. A subclass method with the same name is a brand-new, unrelated method, and putting
@Overrideon it fails to compile. - static: a static method belongs to the class, not to an object. A subclass can declare a static method with the same signature, but that is method hiding: the version that runs is chosen from the reference type at compile time, not from the object at runtime.
@Overrideon a static method is a compile error. - Related: constructors are not inherited, so they cannot be overridden either, and the
mainmethod, being static, can only be hidden.
Calling the Parent Version with super
An override replaces the parent's method body completely, but sometimes you want to add to the parent's work rather than throw it away. Inside the override, super.methodName(...) runs the parent's version, and you can put your own code before or after it. A TimestampLogger can print a time and then call super.log(message) so the original logging logic still runs without being copied. The super keyword lesson covers the other uses of super.
Runtime Polymorphism and Dynamic Method Dispatch
Overriding is what makes runtime polymorphism possible. When you write Payment p = new CardPayment();, the variable's declared type is Payment but the object is a CardPayment. The compiler only checks that Payment has a pay method. At runtime, the JVM looks at the real object and runs CardPayment.pay. This runtime choice is called dynamic method dispatch. It applies only to overridden instance methods; fields, static methods and private methods are all resolved from the reference type at compile time. The next lesson, Runtime Polymorphism and Dynamic Binding, goes deeper.
Overriding vs Overloading
Overriding always involves two classes in an inheritance relationship and an identical signature, resolved at runtime. Overloading always happens within the same class (or via inheritance without changing the return-type-only rule) with a different parameter list, resolved at compile time. Confusing the two is one of the most frequent conceptual errors in Java learning.
- Where: overriding needs a parent and a child class; overloading happens within one class (or a class and its subclass).
- Parameters: overriding keeps them identical; overloading must change them.
- Return type: overriding needs the same type or a covariant subtype; overloading lets it be anything, but a return type alone cannot tell two overloads apart.
- Binding: overriding is resolved at runtime (dynamic dispatch); overloading is resolved at compile time.
- static, final, private: cannot be overridden; can all be overloaded.
Examples
A valid override with a covariant return type
class Shape {
double area() {
return 0.0;
}
Shape createCopy() {
return new Shape();
}
}
class Circle extends Shape {
double radius = 5.0;
@Override
double area() {
return Math.PI * radius * radius;
}
@Override
Circle createCopy() { // covariant return type: Circle is a subtype of Shape
return new Circle();
}
public static void main(String[] args) {
Circle c = new Circle();
System.out.printf("Area: %.2f%n", c.area());
}
}
Area: 78.54
One call, many behaviours: overriding in a payment system
public class PaymentDemo {
public static void main(String[] args) {
Payment[] payments = { new Payment(), new CardPayment(), new UpiPayment() };
for (Payment p : payments) {
p.pay(500); // one call, three different behaviours
}
}
}
class Payment {
void pay(int amount) {
System.out.println("Paying " + amount + " by a generic method");
}
}
class CardPayment extends Payment {
@Override
void pay(int amount) {
System.out.println("Paying " + amount + " by card (2% fee: " + amount * 2 / 100 + ")");
}
}
class UpiPayment extends Payment {
@Override
void pay(int amount) {
System.out.println("Paying " + amount + " by UPI (no fee)");
}
}
Paying 500 by a generic method
Paying 500 by card (2% fee: 10)
Paying 500 by UPI (no fee)
Extending the parent method with super instead of replacing it
public class LoggerDemo {
public static void main(String[] args) {
Logger log = new TimestampLogger();
log.log("Order placed");
}
}
class Logger {
void log(String message) {
System.out.println("LOG: " + message);
}
}
class TimestampLogger extends Logger {
@Override
void log(String message) {
System.out.print("[10:30] ");
super.log(message); // reuse the parent's work instead of copying it
}
}
[10:30] LOG: Order placed
Overriding vs hiding: an instance method and a static method with the same signature
public class HidingDemo {
public static void main(String[] args) {
Parent ref = new Child();
ref.show(); // instance method: overridden, runs Child's version
ref.info(); // static method: hidden, the reference type decides
Child.info(); // call static methods through the class name instead
}
}
class Parent {
void show() {
System.out.println("Parent.show()");
}
static void info() {
System.out.println("Parent.info()");
}
}
class Child extends Parent {
@Override
void show() {
System.out.println("Child.show()");
}
static void info() { // hides Parent.info(); @Override here would not compile
System.out.println("Child.info()");
}
}
Child.show()
Parent.info()
Child.info()
Exception rules that compile: same, narrower, none, or unchecked
import java.io.FileNotFoundException;
import java.io.IOException;
class Reader {
void read() throws IOException { }
}
class SafeReader extends Reader {
@Override
void read() throws FileNotFoundException { } // narrower checked exception: OK
}
class NoThrowReader extends Reader {
@Override
void read() { } // throwing nothing: OK
}
class LoudReader extends Reader {
@Override
void read() throws IllegalStateException { } // any unchecked exception: OK
}
public class Ok {
public static void main(String[] args) {
System.out.println("compiles");
}
}
compiles
The compile errors that break each rule (javac 17)
import java.io.FileNotFoundException;
import java.io.IOException;
class Reader {
public void open() throws FileNotFoundException { }
public void read() throws IOException { }
final void close() { }
static void reset() { }
}
class BadReader extends Reader {
// 1. Broader checked exception than the parent declares
@Override
public void open() throws IOException { }
// 2. Narrower access than the parent (public -> protected)
@Override
protected void read() { }
// 3. Overriding a final method
@Override
void close() { }
// 4. @Override on a static method
@Override
static void reset() { }
}
1. error: open() in BadReader cannot override open() in Reader
public void open() throws IOException { }
^
overridden method does not throw IOException
2. error: read() in BadReader cannot override read() in Reader
protected void read() { }
^
attempting to assign weaker access privileges; was public
3. error: close() in BadReader cannot override close() in Reader
void close() { }
^
overridden method is final
4. error: static methods cannot be annotated with @Override
@Override
^
Common Mistakes
- Slightly changing the parameter list of what was meant to be an override, which accidentally creates an unrelated overload instead — @Override would have caught this at compile time.
- Trying to override a static, final, or private method, none of which can be overridden.
- Reducing the visibility of an overriding method (e.g. from public to protected), which fails to compile.
- Trying to change the return type to an unrelated type instead of a covariant (subtype) return type.
- Declaring a new or broader checked exception (for example
Exceptionwhen the parent declaresIOException, or any checked exception when the parent declares none). - Expecting a static method in the child to run through a parent reference. Static methods are hidden, not overridden, so the reference type decides.
- Forgetting
super.method()in an override that was meant to extend the parent, so the parent's work silently stops happening.
Exercise
This program should print 4 then 2, but it prints 4 twice because the method in Bike is misspelt, so it does not override anything. Add @Override to see the compiler report the problem, then fix the name.
Expected output: 4 then 2
class Vehicle {
int wheels() {
return 4;
}
}
class Bike extends Vehicle {
int wheel() {
return 2;
}
}
public class WheelsDemo {
public static void main(String[] args) {
Vehicle car = new Vehicle();
Vehicle bike = new Bike();
System.out.println(car.wheels());
System.out.println(bike.wheels());
}
}
Show hints
- An override must have exactly the same name and parameter list as the parent method.
- With
@Overrideon a method that overrides nothing, the code will not compile.
Show solution
class Vehicle {
int wheels() {
return 4;
}
}
class Bike extends Vehicle {
@Override
int wheels() {
return 2;
}
}
public class WheelsDemo {
public static void main(String[] args) {
Vehicle car = new Vehicle();
Vehicle bike = new Bike();
System.out.println(car.wheels()); // 4
System.out.println(bike.wheels()); // 2
}
}
Quick quiz
1. A parent method is public. Can the overriding method be protected?
- A. Yes
- B. Only if the class is abstract
- C. No, an override cannot reduce visibility
- D. Only if it is also final
Show answer
C. An override may keep or widen access but never narrow it, otherwise code using the parent type could lose access to the method.
2. Can a private method be overridden?
- A. Yes
- B. Only within the same package
- C. Only with @Override
- D. No, it is not visible to the subclass
Show answer
D. A private method is not inherited. A subclass method with the same name is simply a new, separate method.
3. A parent method returns Shape. Can the override return Circle, a subclass of Shape?
- A. Yes, this is a covariant return type
- B. No, the return type must be identical
- C. Only for abstract methods
- D. Only if Circle is final
Show answer
A. An overriding method may return a subtype of the original return type. This is called a covariant return type.
Interview questions
What are the rules for overriding a method?
Show answer
The method must have the same name and parameter list. The return type must be the same or a subtype. The access level must be the same or wider. It must not throw new or broader checked exceptions than the parent method. And the parent method must be inherited and overridable: not private, not static, and not final.
Can static, final or private methods be overridden?
Show answer
No to all three. A final method is explicitly closed to overriding, and trying is a compile error. A private method is not inherited, so a same-named method in the subclass is unrelated. A static method belongs to the class; a subclass can declare one with the same signature, but that hides the parent's method and is resolved at compile time.
Can an overriding method throw exceptions the parent method does not declare?
Show answer
Unchecked exceptions, yes: any RuntimeException subclass is allowed. Checked exceptions, only if they are the same as or narrower than what the parent declares. If the parent declares IOException, the override may declare FileNotFoundException or nothing, but not Exception. Callers using the parent type must never meet a checked exception they were not told about.
Can a constructor be overridden?
Show answer
No. Constructors are not inherited, and each class's constructor has that class's name, so a subclass can never declare a constructor with the parent's signature. A subclass constructor can call a parent constructor with super(...), and constructors can be overloaded, but not overridden.
What is the difference between method overloading and method overriding?
Show answer
Overloading is several methods with the same name and different parameter lists, usually in one class, chosen by the compiler from the arguments (compile-time polymorphism). Overriding is a subclass redefining an inherited method with the same signature, chosen by the JVM from the actual object at runtime (runtime polymorphism). static, final and private methods can be overloaded but not overridden.
Key Points to Remember
- Overriding requires an identical method signature between superclass and subclass, resolved at runtime.
- The return type must match or be covariant; visibility cannot be reduced; checked exceptions cannot be broadened.
- Unchecked exceptions are unrestricted in an override.
- static, final, and private methods cannot be overridden; a static method with the same signature only hides the parent's.
super.method()runs the parent's version from inside an override.- @Override is optional but lets the compiler catch signature mistakes early.
Go deeper
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering