What is the difference between method overloading and method overriding?
Overloading means several methods in the same class share a name but take different parameters, and the compiler picks one. Overriding means a subclass gives its own version of a method it inherited, with the same signature, and the JVM picks the version at runtime based on the actual object.
Explain it simply
Overloading is like a single "Pay" button in a food delivery app that accepts different inputs. Pay with a UPI ID, pay with a card number and CVV, or pay with wallet balance. Same name, different things you hand over. The app decides which flow to run by looking at what you gave it.
Overriding is like a family recipe for dal that every generation tweaks. The child inherits "make dal" from the parent but cooks it their own way. When someone asks for dal at the child's house, they get the child's version, even though the name of the dish never changed.
Example
class Calculator {
// Overloading: same name, different parameter lists
int add(int a, int b) { return a + b; }
int add(int a, int b, int c) { return a + b + c; }
double add(double a, double b) { return a + b; }
}
class Notification {
String send() { return "Generic notification"; }
}
class SmsNotification extends Notification {
@Override
String send() { return "SMS sent"; } // Overriding: same signature, new behaviour
}
public class Demo {
public static void main(String[] args) {
Calculator c = new Calculator();
System.out.println(c.add(2, 3)); // prints: 5
System.out.println(c.add(2, 3, 4)); // prints: 9
System.out.println(c.add(2.5, 1.5)); // prints: 4.0
Notification n = new SmsNotification();
System.out.println(n.send()); // prints: SMS sent
}
}How it works
| Overloading | Overriding | |
|---|---|---|
| Where | Same class (or inherited methods plus new ones) | Parent and child class |
| Method name | Same | Same |
| Parameters | Must differ in number, type or order | Must be the same |
| Return type | Can differ, but cannot be the only difference | Same, or a subtype (covariant return) |
| Decided | At compile time | At runtime |
| Also called | Compile-time (static) polymorphism | Runtime (dynamic) polymorphism |
Rules for overriding in Java:
- The access level cannot be more restrictive. A
publicmethod cannot becomeprotectedin the child. - The child cannot declare new or broader checked exceptions.
static,privateandfinalmethods cannot be overridden.- Use
@Overrideso the compiler catches typos and signature mismatches.
Real-world use
- Overloading:
System.out.printlnhas versions forint,String,char[],Objectand more.Math.maxworks forint,long,floatanddouble. - Overriding:
toString(),equals()andhashCode()fromObject; each payment type in a gateway providing its ownpay(); each Spring controller overriding behaviour from a base class.
Common mistakes
- Thinking you can overload by changing only the return type.
int total()anddouble total()will not compile together. - Accidentally overloading when you meant to override, for example writing
equals(Student other)instead ofequals(Object other).@Overridewould flag it. - Saying static methods are overridden. A child static method with the same signature hides the parent's, and the reference type decides which one runs.
What to say in the interview
"Overloading is having several methods with the same name but different parameter lists, usually in the same class, and the compiler chooses which to call based on the arguments, so it's compile-time polymorphism. Overriding is when a subclass provides its own implementation of an inherited method with the same signature, and the JVM picks the method based on the actual object at runtime, so it's runtime polymorphism. For overriding, the return type must be the same or a subtype, access can't be narrowed, and I always add @Override so the compiler checks it."
Follow-up questions
- Can we overload the
mainmethod? Can we override it? - Can a constructor be overridden?
- What is a covariant return type?
- Can a private method be overridden?
Go deeper
Read these once the basics above feel easy.
When several overloads could match, the compiler chooses the most specific one in phases: first exact matches and widening (an int can become a long), then autoboxing (int to Integer), and only then varargs. So with print(long x) and print(Integer x), calling print(5) picks print(long), because widening is tried before boxing. Overload choice uses the declared type of the argument, not the runtime type, which is why Object o = "hi"; print(o); calls print(Object) even if print(String) exists.