В чем разница между перегрузкой и переопределением метода в Java?

Ответ

Перегрузка метода (Method Overloading) — это создание нескольких методов в одном классе с одинаковым именем, но разными параметрами (типом, количеством или порядком). Выбор конкретного метода происходит на этапе компиляции (статический полиморфизм).

Переопределение метода (Method Overriding) — это замена реализации метода, унаследованного от родительского класса, в дочернем классе. Сигнатура метода (имя, параметры, возвращаемый тип) должна совпадать. Выбор метода происходит во время выполнения (динамический полиморфизм).

Ключевые различия: Критерий Перегрузка (Overloading) Переопределение (Overriding)
Класс В пределах одного класса В разных классах (иерархия наследования)
Сигнатура Должна отличаться Должна совпадать
Тип полиморфизма Статический (Compile-time) Динамический (Runtime)
Аннотация Не требуется Рекомендуется @Override
Возвращаемый тип Может отличаться Должен быть ковариантным или одинаковым

Пример перегрузки:

class Printer {
    void print(int x) { System.out.println("Целое: " + x); }
    void print(String s) { System.out.println("Строка: " + s); } // Перегрузка
    void print(int x, String s) { System.out.println(x + ": " + s); } // Перегрузка
}

Пример переопределения:

class Animal {
    void makeSound() {
        System.out.println("Животное издает звук");
    }
}

class Dog extends Animal {
    @Override
    void makeSound() { // Переопределение
        System.out.println("Гав!");
    }
}

Ответ 18+ 🔞

Давай разберём эту хуйню, а то у меня уже мозг плавится от этих терминов, как будто я на лекции по высшей математике в общаге в три часа ночи.

Смотри, есть два понятия: перегрузка и переопределение. Это как близнецы, но один — нормальный парень, а второй — ебнутый на всю голову, который взял твою жизнь и пошёл по ней пиздюлиной.

Перегрузка метода (Method Overloading) — это когда ты в одном классе делаешь несколько методов с одним и тем же именем, но они принимают разную хуйню. Типа, ты зовёшь друга: «Вась, пошли бухать!». А он тебе: «На чём? На такси? Пешком? На велике?». Компилятор, этот занудный ботаник, ещё на этапе написания кода смотрит, какую именно хуйню ты ему суёшь, и выбирает подходящий метод. Это называется статический полиморфизм, или, проще говоря, «угадайка на компиляции».

Переопределение метода (Method Overriding) — это уже драма, блядь, целая мыльная опера. Ты берёшь метод, который тебе оставил в наследство папаша-класс, и говоришь: «Пап, твой способ сосать — это пиздец. Я сделаю по-своему, и будет овердохуища круто». Сигнатура (имя, параметры, возвращаемый тип) должна быть точь-в-точь, как у родителя. А вот какая реализация сработает — решается уже во время выполнения программы, когда все объекты бегают как угорелые. Это динамический полиморфизм, или «сюрприз на рантайме, мать его».

Ключевые различия, чтобы не ебать себе мозг: Критерий Перегрузка (Overloading) Переопределение (Overriding)
Где происходит В пределах одного класса, как в однокомнатной квартире Между разными классами (папа и сын), как семейная драма
Сигнатура Должна отличаться (тип, количество или порядок параметров) Должна совпадать, иначе это уже не переопределение, а пиздец
Когда выбирается метод На этапе компиляции (статически) Во время выполнения программы (динамически)
Аннотация Можно без неё, и так сойдёт Желательно ставить @Override, чтобы компилятор не охуел от твоего творчества
Возвращаемый тип Может быть разным Должен быть таким же или ковариантным (подтипом)

Пример перегрузки, чтобы стало совсем ясно: Представь, у тебя есть класс Printer — это такой захудалый принтер в офисе, который всё печатает, но по-разному.

class Printer {
    // Печатает циферку
    void print(int x) { System.out.println("Целое: " + x); }
    // А тут уже строку, это перегрузка, ёпта!
    void print(String s) { System.out.println("Строка: " + s); }
    // А тут и то, и другое — опять перегрузка, ядрёна вошь!
    void print(int x, String s) { System.out.println(x + ": " + s); }
}

Компилятор смотрит, что ты ему передаёшь — int или String — и вызывает нужный метод. Всё просто, как три копейки.

Пример переопределения, где начинается трагедия: Тут у нас семейка. Папаша Animal и его бунтующий сынок Dog.

class Animal {
    void makeSound() {
        System.out.println("Животное издает звук"); // Скукота, блядь
    }
}

class Dog extends Animal {
    @Override
    void makeSound() { // А вот сынок говорит: «Пап, твой звук — хуйня. Я буду гавкать!»
        System.out.println("Гав!"); // Вот это уже дело!
    }
}

Когда программа запустится и встретит объект Dog, она вызовет именно его метод makeSound(), а не папин. Потому что сынок переопределил наследство, хитрая жопа.

Вот и вся разница. Перегрузка — это когда ты один, но можешь по-разному, а переопределение — это когда ты взял чужое и сделал из него свою дичь. Понял? Если нет — иди нахуй, перечитай.