Difference between overloading and overriding — including… — Cracked Java
// Object-Oriented Programming · Inheritance, super, and Method Overriding
JuniorTheoryEPAM

Difference between overloading and overriding — including when each is resolved.

Overloading is multiple methods with the same name and different parameter lists in the same class, resolved at compile time by argument types. Overriding is a subclass replacing a parent method with the same signature, resolved at runtime by the object's class. The first is a naming convenience; the second is the engine of polymorphism.

Definitions side by side

AspectOverloadingOverriding
Where it livesSame class (or inherited as siblings)Subclass replacing parent's method
What differsParameter list (count or types)Nothing — signature must match
Return typeCan differ freelySame or covariant
AccessIndependentSame or wider
Resolved atCompile time (static dispatch)Runtime (dynamic dispatch)
Polymorphism kindAd-hocSubtype
@Override validNoYes — and you should use it

Overloading: compile-time choice

class Printer {
    void print(int n)    { System.out.println("int "    + n); }
    void print(long n)   { System.out.println("long "   + n); }
    void print(Object o) { System.out.println("Object " + o); }
}

Printer p = new Printer();
p.print(42);             // "int 42"     — matches int
p.print(42L);            // "long 42"    — matches long
p.print("hello");        // "Object hello"
Object obj = 42;
p.print(obj);            // "Object 42"  — declared type is Object

Resolution rules: exact match wins, then widening primitive, then autoboxing, then varargs. The declared type of the argument is what the compiler sees.

Overriding: runtime dispatch

class Shape  { double area() { return 0; } }
class Circle extends Shape {
    final double r;
    Circle(double r) { this.r = r; }
    @Override double area() { return Math.PI * r * r; }
}

Shape s = new Circle(3);
s.area();                // 28.27... — Circle.area runs, not Shape.area

The compiler emits invokevirtual Shape.area:()D. At call time the JVM consults the receiver's vtable; the entry for area points to Circle.area.

Combined in one example

class Parent {
    void f(int n)    { System.out.println("Parent int"); }
    void f(Object o) { System.out.println("Parent Object"); }
}

class Child extends Parent {
    @Override void f(int n) { System.out.println("Child int"); }
}

Parent p = new Child();
p.f(1);             // "Child int"     — overload picked at compile (int), override at runtime
p.f("x");           // "Parent Object" — overload picked at compile, no Child override exists

Each call: (1) compiler picks the overload, (2) JVM dispatches that overload's slot on the runtime type.

A common bug from overloading

List<Integer> xs = new ArrayList<>(List.of(1, 2, 3));
xs.remove(1);       // calls remove(int index) — removes element at index 1, leaves [1, 3]
xs.remove(Integer.valueOf(1));   // calls remove(Object) — removes the value 1

Two overloads of remove exist on List, and an int literal silently picks the one that takes an index. Boxing would have been a worse match than no conversion, so the compiler chooses remove(int).

Mark your status