00:00

Difference Between Method Overloading and Method Overriding in Java

Method overloading and method overriding both let one method name have several implementations, but they work in completely different ways:

  • Overloading: same name, different parameter lists, usually in the same class. The compiler picks the method from the argument types (compile-time polymorphism).
  • Overriding: same name and same parameters, in a subclass. The JVM picks the method from the actual object at run time (runtime polymorphism).

1. Comparison Table

AspectOverloadingOverriding
DefinitionSame name, different parametersSubclass redefines an inherited method with the same signature
WhereSame class (or added in a subclass)Superclass/interface and subclass
ParametersMust differ (number, type or order)Must be identical
Return typeCan be anythingSame, or a subtype (covariant)
Access modifierCan be anythingSame or wider
Checked exceptionsCan be anythingSame, narrower or none
ResolvedAt compile time (static binding)At run time (dynamic binding)
Based onDeclared types of argumentsActual class of the object
Polymorphism typeCompile-timeRuntime
Inheritance neededNoYes
static methodsCan be overloadedCannot be overridden (only hidden)
private / final methodsCan be overloadedCannot be overridden
AnnotationNone@Override recommended
PurposeConvenience: one name for related operationsSpecialisation: subclass-specific behaviour

2. Both in One Example

OverloadVsOverride.java
public class OverloadVsOverride {
    static class Printer {
        // OVERLOADING: same name, different parameters, same class
        void print(String text)       { System.out.println("Printer text:   " + text); }
        void print(String text, int n) { for (int i = 0; i < n; i++) print(text); }

        // To be OVERRIDDEN by the subclass
        String header()               { return "Plain"; }
        void printHeader()            { System.out.println("Header: " + header()); }
    }

    static class ColorPrinter extends Printer {
        // OVERRIDING: same signature, subclass, decided at run time
        @Override String header()     { return "Color"; }

        // OVERLOADING across inheritance: a new parameter list
        void print(String text, String color) { System.out.println("Printer " + color + ": " + text); }
    }

    public static void main(String[] args) {
        Printer p = new ColorPrinter();     // reference type Printer, object type ColorPrinter
        p.print("Hello");                   // overload chosen by compiler
        p.print("Hi", 2);                   // another overload
        p.printHeader();                    // override chosen at run time -> Color
        // p.print("x", "red");             // compile error: Printer has no print(String, String)
        ((ColorPrinter) p).print("Hello", "red");
    }
}
Output
Printer text:   Hello
Printer text:   Hi
Printer text:   Hi
Header: Color
Printer red: Hello
  • print(String) and print(String, int) are overloads: the compiler chooses between them.
  • header() is overridden: even though p is declared as Printer, the ColorPrinter version runs.
  • print(String, String) in the subclass is an overload added through inheritance. It is not visible through a Printer reference.

3. Compile-Time vs Run-Time Choice

The clearest way to see the difference is to combine both in one call. The overload is picked by the declared type of the argument; the override is picked by the actual object:

BindingTimeDemo.java
public class BindingTimeDemo {
    static class Animal { String speak() { return "..."; } }
    static class Dog extends Animal { @Override String speak() { return "Woof"; } }

    // Overloads: chosen by the DECLARED type of the argument (compile time)
    static String describe(Animal a) { return "describe(Animal) -> " + a.speak(); }
    static String describe(Dog d)    { return "describe(Dog)    -> " + d.speak(); }

    public static void main(String[] args) {
        Animal a = new Dog();     // declared Animal, actually a Dog
        Dog d = new Dog();
        System.out.println(describe(a));   // overload by declared type: Animal; override by object: Woof
        System.out.println(describe(d));
    }
}
Output
describe(Animal) -> Woof
describe(Dog)    -> Woof

For describe(a), the compiler only knows a is an Animal, so it selects describe(Animal). Inside, a.speak() is an overridden method, so the JVM runs Dog.speak().

4. When to Use Which

  • Use overloading to offer convenient variants of the same operation: log(String), log(String, Throwable); constructors with optional parameters.
  • Use overriding when a subclass must behave differently for an operation defined by the parent or interface: toString(), area(), processPayment().

5. Common Mistakes

  • Writing equals(MyClass o) instead of equals(Object o): that overloads instead of overriding. @Override catches it.
  • Expecting overloads to be chosen by runtime type; they are not.
  • Trying to overload by return type only, which does not compile.
  • Expecting static methods to behave polymorphically.

6. Summary

  • Overloading: same name, different parameters; compile-time; no inheritance needed.
  • Overriding: same signature in a subclass; run-time; requires inheritance.
  • Overriding has strict rules for return types, access and exceptions; overloading has none beyond the parameter list.
  • Static, private and final methods can be overloaded but not overridden.

Related Tutorials