Method overloading means one class has several methods with the same name but different parameter lists, and method overriding means a subclass replaces the body of an inherited instance method with the same signature. The compiler picks an overloaded method from the argument types at compile time, whereas the JVM picks an overridden method from the runtime class of the object.
We use overloading to offer the same operation for different inputs, such as String.valueOf(int) and String.valueOf(char[]). We use overriding to let each subclass do its own version of an inherited operation, which is the basis of runtime polymorphism in Java.
The following example uses JDK classes to show both mechanisms in four lines.
String fromInt = String.valueOf(42); // "42"
String fromChars = String.valueOf(new char[] {'h', 'i'}); // "hi"
Object drinks = List.of("tea", "milk");
String text = drinks.toString(); // "[tea, milk]"
Notice that the first two calls go to two different valueOf() methods chosen by the argument type, while the last call goes through an Object reference and still runs the toString() of the list class. We compare both features side by side, go through the rules of each one, show how the compiler resolves overloads, and finish with the traps that show up in interviews and code reviews.
1. Method Overloading vs Overriding at a Glance
Both features reuse a method name, so they look alike in code. The difference is who makes the decision and when. For an overloaded call, javac looks at the declared types of the arguments and writes the chosen method into the bytecode. For an overridden method, the bytecode only names the method, and the JVM looks up the body in the class of the actual object when the call runs.

| Aspect | Method overloading | Method overriding |
|---|---|---|
| Where | Same class, or a subclass that adds another version | Subclass of the class or interface that declares the method |
| Method name | Same | Same |
| Parameter list | Must differ in number, type or order | Must be the same (after type erasure) |
| Return type | Any, it is not part of the decision | Same type, or a subtype for reference types (covariant return) |
| Access modifier | Any | Same or wider, never narrower |
| Checked exceptions | Any | Same, narrower or none, never new or broader ones |
| static, private, final methods | Can be overloaded | Cannot be overridden |
| Decided by | Compiler, from the declared argument types | JVM, from the runtime class of the object |
| Also called | Compile-time (static) polymorphism | Runtime (dynamic) polymorphism |
A ticket booking app shows the difference well. The booking service has overloaded price() methods for one ticket, for several tickets and for a group with a coupon, so callers pass whatever they have. The ticket types, such as student and senior tickets, override one basePrice() method, so the checkout code works with plain Ticket references and never asks which type it holds.
2. Method Overloading in Java
Overloading gives one name to a group of methods that do the same job for different inputs. The name plus the parameter types form the method signature, and every method in a class needs a unique signature, so the parameter lists must differ. Overloading has nothing to do with inheritance, although a subclass can add one more overload to an inherited method name.
The following example is a Fares utility with three overloads of price(). The first takes the number of tickets, the second adds a discount percentage, and the third takes the arguments in a different order and type.
class Fares {
static double price(int tickets) {
return tickets * 12.0;
}
static double price(int tickets, int discountPercent) {
return price(tickets) * (100 - discountPercent) / 100;
}
static double price(String coupon, int tickets) {
return coupon.equals("HALF") ? price(tickets, 50) : price(tickets);
}
}
double two = Fares.price(2); // 24.0
double reduced = Fares.price(2, 25); // 18.0
double coupon = Fares.price("HALF", 4); // 24.0
Each call compiles against one specific method. The overloads also call each other, which keeps the price calculation in one place instead of three copies.
2.1. What Makes Two Overloads Different
Only the parameter list counts. Two methods are valid overloads when they differ in one of these points.
- The number of parameters, such as price(int) and price(int, int).
- The type of at least one parameter, such as price(int) and price(double).
- The order of parameters with different types, such as price(String, int) and price(int, String).
Parameter names, the return type, the access modifier and the throws clause are not part of the signature. Two methods that differ only in those points clash, and the class does not compile.
// does not compile: price(int) is already defined in class Fares
class Fares {
static double price(int tickets) { return tickets * 12.0; }
static long price(int count) { return count * 12L; }
}
The javac error reads method price(int) is already defined in class Fares. The same error appears when two methods differ only in the exceptions they declare. Generic parameters also count after type erasure, so total(List<Integer>) and total(List<Double>) fail with name clash … have the same erasure, because both erase to total(List).
2.2. How the Compiler Picks an Overload
When the argument types do not match a parameter list one to one, the compiler searches in three phases defined in JLS 15.12.2. It stops at the first phase that finds a method, so a later phase never wins over an earlier one.
- Phase 1 allows primitive widening, such as int to long, but no boxing and no varargs.
- Phase 2 also allows boxing and unboxing, such as int to Integer.
- Phase 3 also allows varargs methods.
Inside a phase, the most specific method wins. For example, String is more specific than Object, so a String argument goes to the String overload.
static String kind(long value) {
return "long";
}
static String kind(Integer value) {
return "Integer";
}
static String kind(int... values) {
return "varargs";
}
static String kind(Object value) {
return "Object";
}
String widened = kind(5); // "long"
String boxed = kind(Integer.valueOf(5)); // "Integer"
String text = kind("five"); // "Object"
String none = kind(); // "varargs"
The call kind(5) surprises most people. The literal is an int, and widening to long happens in phase 1, so the compiler never looks at the Integer overload. When two methods are equally specific, the call is ambiguous. For example, with find(String) and find(Integer), the call find(null) fails with reference to find is ambiguous, and a cast such as find((String) null) fixes it.
The JDK has a real trap of the same kind in List, which has both remove(int index) and remove(Object o). With a List<Integer>, an int argument picks the index version.
List<Integer> seats = new ArrayList<>(List.of(10, 20, 30));
Integer removedAt = seats.remove(1); // 20, removes index 1
boolean removed = seats.remove(Integer.valueOf(10)); // true, removes the value 10
List<Integer> left = seats; // [30]
When an overload set mixes primitives, wrapper types and Object, pass an argument of the exact parameter type, so the compiler cannot choose a different method.
3. Method Overriding in Java
Overriding lets a subclass give its own body to an instance method it inherits from a superclass or an interface. A reference of the parent type can point to an object of any subclass, and every call runs the version from the class of that object. So the calling code stays the same when we add a new subclass.
The following example has a Ticket base class with a basePrice() method and a label() method that uses it. The StudentTicket subclass overrides basePrice() and calls the parent version with super.
class Ticket {
double basePrice() {
return 12.0;
}
String label() {
return getClass().getSimpleName() + " " + basePrice();
}
}
class StudentTicket extends Ticket {
@Override
double basePrice() {
return super.basePrice() * 0.5;
}
}
Ticket regular = new Ticket();
Ticket student = new StudentTicket();
double full = regular.basePrice(); // 12.0
double half = student.basePrice(); // 6.0
String tag = student.label(); // "StudentTicket 6.0"
The variable student is declared as Ticket, but the object is a StudentTicket, so basePrice() runs the subclass body. The call inside label() also goes to the override, even though label() is written in Ticket. The rules for a valid override are in JLS 8.4.8.3, and the next subsections cover them one by one.
3.1. Same Parameters, Same or Narrower Return Type
The overriding method must have the same name and the same parameter types. If the parameter types differ, the subclass gets a new overload instead of an override. The return type can be the same or, for reference types, a subtype of the original, which is called a covariant return type. A primitive return type must stay the same.
class Booking {
Ticket ticket() { return new Ticket(); }
}
class StudentBooking extends Booking {
@Override
StudentTicket ticket() { return new StudentTicket(); }
}
StudentTicket cheap = new StudentBooking().ticket(); // a StudentTicket, no cast
double cheapPrice = cheap.basePrice(); // 6.0
An incompatible return type, such as String in place of Ticket, is a compile error that says return type String is not compatible with Ticket.
3.2. Access Can Only Get Wider
Code that holds a parent reference must be able to call every method the parent declares. So an override keeps the same access level or widens it, in the order private, package-private, protected, public. A protected method can become public in the subclass, but a public method cannot become protected.
// does not compile: attempting to assign weaker access privileges; was public
class Kiosk {
public String greet() { return "Hi"; }
}
class QuietKiosk extends Kiosk {
protected String greet() { return "..."; }
}
3.3. Checked Exceptions Can Only Get Narrower
An override may declare the same checked exceptions, subclasses of them, or none at all. It may not add a new checked exception or a broader one, because callers of the parent method only handle what the parent declares. Unchecked exceptions, such as IllegalStateException and other subclasses of RuntimeException, are not restricted.
class SeatMap {
String load(String hall) throws IOException { return hall + " loaded"; }
}
class CachedSeatMap extends SeatMap {
@Override
String load(String hall) { return hall + " from cache"; } // throws nothing
}
SeatMap map = new CachedSeatMap();
String loaded = map.load("A"); // "A from cache"
The call still needs a try block or a throws clause around it when the declared type is SeatMap, because the compiler checks against the parent’s throws IOException. If CachedSeatMap.load() declared throws Exception, the error would read overridden method does not throw Exception.
3.4. Methods That Cannot Be Overridden
Three kinds of methods are never overridden, and the compiler treats each one differently.
- A final method cannot be overridden, and a subclass method with the same signature is a compile error.
- A private method is not inherited, so a subclass method with the same signature is a new, unrelated method and compiles without any error.
- A static method belongs to the class, so a subclass static method with the same signature hides it instead of overriding it, which is covered in method hiding.
Constructors are not inherited either, so they cannot be overridden, although a class can overload them.
3.5. Let @Override Catch Mistakes
The @Override annotation asks the compiler to check that the method overrides something. Without it, a typo in the name or a wrong parameter type creates an overload, and the code compiles while the parent method keeps running. The classic case is equals(), which must take an Object parameter.
class Seat {
final String code;
Seat(String code) { this.code = code; }
public boolean equals(Seat other) { // overload, not override
return other != null && code.equals(other.code);
}
}
Seat seat = new Seat("A1");
boolean typed = seat.equals(new Seat("A1")); // true
Object asObject = new Seat("A1");
boolean viaObject = seat.equals(asObject); // false
boolean inList = List.of(new Seat("A1")).contains(seat); // false
The contains() method calls equals(Object), which is still the identity check inherited from Object, so the list never finds the seat. Adding @Override to the method turns the bug into the compile error method does not override or implement a method from a supertype. Changing the parameter to Object fixes it, and a matching hashCode() override goes with it.
4. When Overloading and Overriding Meet
The two mechanisms run one after the other on every instance method call. First the compiler picks the overload from the declared types of the arguments. After that, the JVM picks the override of that one overload from the runtime class of the receiver object. The runtime class of an argument never changes the chosen overload.
The following example has a Printer with two describe() overloads and a LabelPrinter subclass that overrides only the Object version.
class Printer {
String describe(Object item) { return "Printer prints an object"; }
String describe(String item) { return "Printer prints text"; }
}
class LabelPrinter extends Printer {
@Override
String describe(Object item) { return "LabelPrinter prints an object"; }
}
Printer printer = new LabelPrinter();
Object item = "ticket";
String first = printer.describe(item); // "LabelPrinter prints an object"
String second = printer.describe("ticket"); // "Printer prints text"
In the first call, the variable item is declared as Object, so the compiler picks describe(Object), although the object is a String. The JVM runs the LabelPrinter override of that overload. In the second call, the argument type is String, so the compiler picks describe(String), which LabelPrinter does not override. Both features build on inheritance and the other ideas in our object-oriented programming guide.
5. Overloading and Overriding FAQs
Interviewers who ask for the difference rarely stop at the definitions, and these Java interview follow-ups each test one rule from the sections above.
5.1. Can we overload the main() method?
Yes. A class can declare main(int) or main(String, int) next to the standard entry point. The launcher still starts main(String[]), and the other overloads run only when our code calls them. Since Java 25, the launcher also accepts a main() without parameters (JEP 512), and it picks main(String[]) when a class declares both.
5.2. Can we override a static method?
No. A subclass static method with the same signature hides the parent method, and the call is resolved from the declared type of the reference at compile time. Declaring an instance method that matches a parent static method, or the reverse, is a compile error.
5.3. Can overloaded methods differ only in the return type?
No. The return type is not part of the signature, so int count() and long count() in one class fail with method count() is already defined. The parameter list must differ.
5.4. What happens if a subclass redeclares a private method?
The subclass gets a new, unrelated method. A private method is not visible to subclasses, so nothing is overridden. Adding @Override to it makes the compiler report that it does not override anything.
5.5. Can a constructor be overloaded or overridden?
A constructor can be overloaded but not overridden. A class can have several constructors with different parameters, and they can call each other with this(…). Constructors are not inherited, so a subclass has nothing to override and calls a parent constructor with super(…) instead.
5.6. Which one is faster, overloading or overriding?
The difference does not matter in practice. An overloaded call is linked at compile time. An overridden call needs a lookup in the class at runtime, but the JIT compiler inlines most of these calls when only one or two receiver classes show up at a call site.
6. Conclusion
Overloading and overriding both reuse a method name, but they answer different questions. Overloading offers one operation for different parameter lists, and the compiler fixes the choice from the declared argument types. Overriding replaces an inherited instance method, and the JVM picks the body from the class of the object on every call.
Most bugs around the two come from mixing them up. An overload set with int, long, Integer and Object parameters can pick a method we did not expect, and a method that looks like an override but has a different parameter type never runs through a parent reference. Putting @Override on every overriding method makes the compiler catch the second problem for us.
7. References
- JLS 25, Inheritance, Overriding, and Hiding
- JLS 25, Overloading
- JLS 25, Compile-Time Step 2 of a Method Invocation
- Oracle Java Tutorials, Overriding and Hiding Methods
- Override Javadoc
Happy Learning !!
Hi,
why I am not getting any compile time exception in this example? coz in super class we are using “FileNotFoundException” and in subclass while overriding we have “IOException”
package Overriding;
import java.io.FileNotFoundException;
import java.io.IOException;
class SuperClass1{
public Number sum(int a,int b)throws FileNotFoundException{
System.out.println(“super”);
return a+b;
}
}
public class SubClass1 {
/*
public static void main(String[] args)throws IOException {
SubClass1 sc= new SubClass1();
sc.sum(10, 30);
}*/
public Integer sum(int a,int b)throws IOException{
System.out.println(“sub”);
return a+b;
}
}
subclass should extend super class first…:)
The below mentioned code works perfectly in JDK1.6 . I Just want to know is it overloading ??
public class TTTT {
public void display(){
System.out.println(“Inside display of TTTT class”);
}
public int display(int i){
System.out.println(“Inside display of TTTT class values is “+ i);
return 0;
}
}
Yes it is.
why String literal and String object return same hashcode? what is the internal process
String literals are short-hand representation of string objects only. They are used for performance gain using String pool concept.
Read More : https://howtodoinjava.com/interview-questions/string-questions/
Hi Lokesh,
Thank you for posting about rules for overriding a method in java.
I have a doubt If super class method is default and sub class method is in another package. Its tries to override the super class method what will happen ?
As we discussed method overriding in same package but we missed that if sub class is in another package..
Thanks in advance..
default methods are always like private methods in different package. As you can not override private methods (even in same package), so you can not override default method (only in different package). Thanks for pointing out.