Method overloading means one class has several methods with the same name but different parameter lists; method overriding means a subclass redefines a method it inherits, keeping the same name and parameters, so its own version runs instead. Java picks an overload while it compiles, from the types of the arguments, and picks an override while the program runs, from the class of the actual object. Below, both run in one short program, a table compares them rule by rule, and the later sections cover the classic mix-up with equals and the questions about main and about static, private and final methods.
public class Main { public static void main(String[] args) { Calculator calc = new Calculator(); System.out.println(calc.add(2, 3)); System.out.println(calc.add(2.5, 1.5)); Animal pet = new Dog(); System.out.println(pet.sound()); }} class Calculator { int add(int a, int b) { return a + b; } double add(double a, double b) { return a + b; }} class Animal { String sound() { return "..."; }} class Dog extends Animal { @Override String sound() { return "Woof"; }}It prints 5, 4.0 and Woof. The two add methods are overloads: they share a name, take different parameter types, and the arguments of each call decide which one runs. sound() is overridden: Dog replaces the version it inherits from Animal, and because the object in pet is a Dog, the dog's version runs even though the variable is declared as an Animal.
Method overloading in Java, with an example#
Overloading is giving several methods in one class the same name, as long as their parameter lists differ: a different number of parameters, different types, or the same types in a different order. For each call, Java looks at the arguments and runs the version that fits them. Here a Calculator has three methods named add.
public class Main { public static void main(String[] args) { Calculator calc = new Calculator(); System.out.println(calc.add(2, 3)); System.out.println(calc.add(2.5, 1.5)); System.out.println(calc.add(1, 2, 3)); System.out.println(calc.add(2, 3.5)); }} class Calculator { int add(int a, int b) { return a + b; } double add(double a, double b) { return a + b; } int add(int a, int b, int c) { return a + b + c; }}It prints 5, 4.0, 6 and 5.5. Step through it and watch which add each call jumps into. Two whole numbers go to add(int, int), two decimal numbers to add(double, double), and three arguments to the three-parameter version. The last call, add(2, 3.5), has no exact match, so Java widens the 2 to 2.0 and uses the double version. When more than one version could take the arguments, Java picks the closest fit: add(2, 3) could also run as two doubles, but add(int, int) matches exactly, so it wins.
All of this is settled while the program compiles. javac reads the types of the arguments in your source code and fixes which add each call will run before the program ever starts. System.out.println is overloaded the same way, with one version for an int, one for a double, one for a String and several more. Constructors can be overloaded too, as the constructors lesson shows.
A different return type is not an overload#
A method's signature is its name plus the types of its parameters, in order. Overloads must have different signatures, and the return type is not part of the signature, nor are the parameter names. So two methods that differ only in what they return do not compile: a call such as add(2, 3) would match both.
public class Main { public static void main(String[] args) { Calculator calc = new Calculator(); System.out.println(calc.add(2, 3)); }} class Calculator { int add(int a, int b) { return a + b; } double add(int a, int b) { return a + b; }}Main.java:13: error: method add(int,int) is already defined in class Calculator
double add(int a, int b) {
^
1 errorMethod overriding in Java, with an example#
Overriding happens between a parent class and a subclass. When a subclass declares a method with the same name and parameter list as one it inherits, the subclass's version overridesthe parent's: for objects of the subclass, the new version runs instead. Here Dog and Cat both override the sound() they inherit from Animal.
public class Main { public static void main(String[] args) { Animal pet = new Animal(); System.out.println(pet.sound()); pet = new Dog(); System.out.println(pet.sound()); pet = new Cat(); System.out.println(pet.sound()); }} class Animal { String sound() { return "..."; }} class Dog extends Animal { @Override String sound() { return "Woof"; }} class Cat extends Animal { @Override String sound() { return "Meow"; }}It prints ..., then Woof, then Meow. The variable pet is declared once, as an Animal, and the line pet.sound() is the same all three times. What changes is the object pet points at, and Java looks at that object each time the line runs: a plain Animal runs Animal's version, a Dog runs Dog's. Choosing the version from the object while the program runs is called dynamic dispatch.
The @Override line above each override is an annotation, a note to the compiler. It changes nothing when the program runs, but it makes javac check that the method really overrides one it inherits, and stop with an error if it does not. Put it on every override; the section on equals below shows the bug it catches.
Why use overriding?#
Overriding lets code written for the parent type work for every subclass, including ones added later. A loop over an Animal[] can call sound() on each element without asking what kind of animal it is, and each object answers in its own way. That is polymorphism, and overriding is what makes it work.
An override can still use the version it replaced: inside Dog, super.sound() runs Animal's sound(). That and the other uses of the keyword are in this vs super in Java.
Overloading vs overriding, rule by rule#
Both reuse a method name, and almost every other rule differs:
| Overloading | Overriding | |
|---|---|---|
| Where the methods are | Usually in one class. A subclass can also add an overload of a method it inherits. | One in a parent class, one in a subclass. |
| Parameter lists | Must differ: in number, in types, or in the order of the types. | Must be the same. |
| Return type | Anything. It does not tell overloads apart. | The same type. For an object type it may be a subtype, such as Dog where the parent returns Animal. |
| Access modifier | Anything. | The same or wider, never narrower. An override of a public method must be public. |
static, private and final methods | All can be overloaded. | None can be overridden. A static one is hidden instead, a private one is not inherited, and a final one is locked. |
| Checked exceptions | Anything. | May not declare new or broader checked exceptions than the parent's method. |
| When Java picks the version | While compiling, from the declared types of the arguments. | While running, from the class of the actual object. |
| Often called | Compile-time, or static, polymorphism. | Runtime, or dynamic, polymorphism. |
@Override | Not used. On a method that overrides nothing, javac reports an error. | Put it on every override, so javac checks that it really is one. |
| Example | add(int, int) next to add(double, double) | Dog.sound() replacing Animal.sound() |
Two of those rows need more explanation. An override may return a more specific type, called a covariant return type: if Animal has Animal makeBaby(), then Dog may override it as Dog makeBaby(), because every Dog is an Animal. For void and primitive types such as int, the return type must match exactly. A checked exception is one the compiler makes you catch or declare with throws, such as IOException. Code that calls pet.sound() through an Animal variable only prepares for the exceptions Animal.sound() declares, so an override may not add new ones; javac stops with overridden method does not throw Exception if it tries.
Key ideaOverloading is about parameter lists, and the compiler settles it. Overriding is about inheritance, and it is settled while the program runs.
Overloads follow the variable, overrides follow the object#
Because the two choices happen at different times, they can disagree in one program. Below, a Vet has two overloads of check, one for any Animal and one for a Dog. The variable pet holds a Dog, but its declared type is Animal. Decide what the two lines print, then run it.
public class Main { public static void main(String[] args) { Vet vet = new Vet(); Animal pet = new Dog(); vet.check(pet); System.out.println(pet.sound()); }} class Vet { void check(Animal a) { System.out.println("check(Animal) runs"); } void check(Dog d) { System.out.println("check(Dog) runs"); }} class Animal { String sound() { return "..."; }} class Dog extends Animal { @Override String sound() { return "Woof"; }}The object in pet is a Dog, and the variable is declared as an Animal. What does the program print?
It prints check(Animal) runs, then Woof. The overload is fixed while compiling, and all the compiler knows about pet is its declared type, Animal, so check(Animal) runs even though the object is a dog. The override is chosen while running, from the object itself, so pet.sound() barks. Declare the variable as Dog pet = new Dog(); and the first line becomes check(Dog) runs.
Key ideaThe declared types of the arguments pick the overload. The class of the object picks the override.
The equals trap: overloading when you meant to override#
Here is the mix-up that causes real bugs. Every class inherits equals(Object o) from Object, and the Java library calls it to compare objects: contains, indexOf and remove on a list, and the keys of a HashMap. To make two dogs with the same name count as equal, you override it. The trap is to write the parameter as a Dog:
import java.util.ArrayList;import java.util.List; public class Main { public static void main(String[] args) { Dog a = new Dog("Rex"); Dog b = new Dog("Rex"); System.out.println(a.equals(b)); List<Dog> kennel = new ArrayList<>(); kennel.add(a); System.out.println(kennel.contains(b)); }} class Dog { String name; Dog(String name) { this.name = name; } public boolean equals(Dog other) { return name.equals(other.name); }}It prints true, then false. equals(Dog other) has a different parameter list from equals(Object o), so it is an overload sitting next to the inherited method, and it overrides nothing. The first call passes b, declared as a Dog, so the compiler picks your equals(Dog). contains is library code that calls equals(Object), which Dog never overrode, so Object's version runs, and it only says true for the very same object. Step through it: the first comparison jumps into your method, and the contains call never enters Dog at all.
Put @Override above the method and the bug becomes a compile error, because javac checks the claim and finds nothing to override:
import java.util.ArrayList;import java.util.List; public class Main { public static void main(String[] args) { Dog a = new Dog("Rex"); Dog b = new Dog("Rex"); System.out.println(a.equals(b)); List<Dog> kennel = new ArrayList<>(); kennel.add(a); System.out.println(kennel.contains(b)); }} class Dog { String name; Dog(String name) { this.name = name; } @Override public boolean equals(Dog other) { return name.equals(other.name); }}Main.java:23: error: method does not override or implement a method from a supertype
@Override
^
1 errorFrom Java 26 on, the message also names the method: equals(Dog) in Dog does not override or implement a method from a supertype. The fix is to take an Object, exactly as Object.equals does, and check the type inside:
import java.util.ArrayList;import java.util.List; public class Main { public static void main(String[] args) { Dog a = new Dog("Rex"); Dog b = new Dog("Rex"); System.out.println(a.equals(b)); List<Dog> kennel = new ArrayList<>(); kennel.add(a); System.out.println(kennel.contains(b)); }} class Dog { String name; Dog(String name) { this.name = name; } @Override public boolean equals(Object o) { if (!(o instanceof Dog other)) { return false; } return name.equals(other.name); } @Override public int hashCode() { return name.hashCode(); }}Now both lines print true, and stepping through shows contains calling your equals. o instanceof Dog other checks that o is a Dog and, if it is, gives it to you as other; that form of instanceof arrived in Java 16. The hashCode override belongs with it: two objects that are equal must return the same hash code, or a HashMap or HashSet can fail to find them. The equals, hashCode and toString lesson builds both step by step.
Object declares equals, hashCode and toString as public, and an override may not be less visible than the method it replaces. Leave the word out and javac stops with attempting to assign weaker access privileges; was public.Can you overload the main method?#
Yes. main is an ordinary method name, so a class can have other methods called main with different parameters. Only one of them starts the program: when you run a class, Java looks for the main that takes a String[] and calls it. The other overloads run only when your code calls them.
public class Main { public static void main(String[] args) { System.out.println("main(String[] args) starts the program"); main(3); } static void main(int times) { System.out.println("main(int) is an ordinary method, called with " + times); }}It prints main(String[] args) starts the program, then main(int) is an ordinary method, called with 3. Since Java 25, a class with no main(String[] args) can also start from a main() that takes no parameters, but when both exist, the String[] version wins. Overriding main is a different question, and the answer is no, because main is static.
Can you override a static, private or final method?#
No. A static method is hidden instead of overridden, a private method is not inherited, and a final method is locked. Static methods are the ones that surprise people. A static method belongs to the class rather than to any object, so there is no object to choose a version from. When a subclass declares a static method with the same signature, it hidesthe parent's: the version that runs is fixed while compiling, from the types written in the code, like an overload.
public class Main { public static void main(String[] args) { Dog rex = new Dog(); System.out.println(rex.describe()); System.out.println(Animal.kind()); System.out.println(Dog.kind()); }} class Animal { static String kind() { return "animal"; } String sound() { return "..."; } String describe() { return "kind() gives " + kind() + ", sound() gives " + sound(); }} class Dog extends Animal { static String kind() { return "dog"; } @Override String sound() { return "Woof"; }}It prints kind() gives animal, sound() gives Woof, then animal, then dog. describe() is written in Animal and runs on a dog. Its call to kind() stays in Animal, because a static method is never looked up from the object: inside Animal's code, kind() means Animal.kind(). Its call to sound() goes to Dog's version, because sound() is overridden and the object is a dog. Step through it and watch kind() stay in Animal while sound() jumps down into Dog. Dog.kind() still exists, and runs when you name the class, as the last line shows.
- A
staticmethod cannot carry@Override: javac reportsstatic methods cannot be annotated with @Override. The static and final lesson covers what static means. - A
privatemethod is invisible to subclasses, so a subclass method with the same name is a brand new method. Put@Overrideon it and javac reportsmethod does not override or implement a method from a supertype. - A
finalmethod is inherited but cannot be replaced. A parent marks a methodfinalto forbid overriding it, and javac stops any subclass that tries withoverridden method is final.
Override vs overload vs overwrite#
Only the first two are Java terms. Overloading and overriding are the method features on this page; overwrite is not something the language does to methods. In everyday programming it means replacing stored data, such as giving a variable a new value or writing a file so that its old contents are gone, as in how to write to a file in Java. When someone says a subclass “overwrites” a method, they mean it overrides it.
Questions people ask#
- What is the difference between method overloading and overriding in Java?
- Overloading is several methods in one class with the same name and different parameter lists, and Java picks one while compiling, from the types of the arguments. Overriding is a subclass redefining a method it inherits, with the same name and parameters, and Java picks the version while the program runs, from the class of the actual object.
- What is an example of method overloading?
- A
Calculatorclass withadd(int a, int b),add(double a, double b)andadd(int a, int b, int c)overloadsadd. The calladd(2, 3)runs the first version,add(2.5, 1.5)the second andadd(1, 2, 3)the third. - Are method overloading and method overriding the same thing?
- No. Overloading adds another method with the same name and a different parameter list, and every version stays callable. Overriding replaces an inherited method in a subclass, so objects of that subclass run the new version instead of the parent's.
- Why use method overriding in Java?
- Overriding lets each subclass do its own version of a shared action, so code written for the parent type, such as a loop over an
Animal[], runs the right version for every object without checking its class. This is what runtime polymorphism means. - Can a static method be overridden in Java?
- No. A static method with the same signature in a subclass hides the parent's method instead, so Java fixes the version while compiling, from the types written in the code, never from the object. Putting
@Overrideon a static method is a compile error. - Can the main method be overloaded in Java?
- Yes. A class can have other methods named
mainwith different parameters, and they are ordinary methods that run only when your code calls them. When the program starts, Java runs themainthat takes aString[].
Further reading#
- Overriding and Hiding Methods, the dev.java tutorial on overrides, hidden static methods and the access rules
- Overloading Methods, from the dev.java tutorial on defining methods
- Inheritance, Overriding, and Hiding, section 8.4.8 of the Java Language Specification for Java SE 25
- The Override annotation in the Java SE 25 API documentation