Перегрузка и переопределение
Это два разных слова, которые легко перепутать — но они про совершенно разные вещи.
Перегрузка (overload) — несколько методов с одним именем, но разными параметрами, в одном классе:
void print(String s) { ... }
void print(int n) { ... }
void print(String s, int n) { ... }
Java сама выбирает нужную версию по тому, что ты передал при вызове. Подробнее — в теме «Перегрузка».
Переопределение (override) — наследник заменяет поведение метода родителя, оставляя ту же сигнатуру:
class Animal {
void makeSound() { System.out.println("..."); }
}
class Dog extends Animal {
@Override
void makeSound() { System.out.println("Гав!"); } // заменили поведение
}
@Override — не обязателен формально, но всегда пиши его: если опечатался в имени метода, компилятор сразу подскажет ошибку, а не тихо создаст новый метод вместо переопределения.
Копнуть глубже
Главное отличие на уровне момента принятия решения:
| Перегрузка | Переопределение | |
|---|---|---|
| Где | в одном классе | родитель → наследник |
| Что меняется | параметры | тело метода |
| Когда выбирается | на этапе компиляции | на этапе выполнения |
| Сигнатура | разная | та же |
Переопределяемый метод должен иметь точно такие же параметры и совместимый тип возврата — иначе это случайно станет перегрузкой, а не переопределением (классическая ошибка новичка).
Под капотом
Перегрузка решается статически — компилятор смотрит на типы аргументов в момент компиляции и сразу “зашивает” в байт-код вызов конкретного метода.
Переопределение решается динамически — через виртуальную таблицу методов (vtable) объекта в рантайме. JVM смотрит на реальный класс объекта (не тип переменной) и вызывает версию метода именно этого класса. Это и есть полиморфизм в действии — подробнее в теме «Принципы ООП».
• почему перегрузка решается на компиляции, а переопределение — в рантайме (если дошёл до 2-го слоя).