В чем разница между интерфейсом и абстрактным классом в Java?

Ответ

Оба механизма используются для достижения абстракции, но служат разным целям и имеют строгие технические отличия.

Критерий Интерфейс (до Java 8+) Абстрактный класс
Наследование Класс может реализовывать несколько интерфейсов (implements I1, I2). Класс может наследовать только один абстрактный класс (extends).
Состояние (поля) Может содержать только public static final константы. Может содержать поля любого модификатора доступа (экземплярные переменные).
Поведение (методы) До Java 8: только абстрактные методы.
С Java 8+: default и static методы с реализацией.
Может содержать абстрактные методы (без тела) и полностью реализованные методы.
Конструктор Не может иметь конструкторов. Имеет конструктор (вызывается при создании экземпляра наследника).
Модификаторы методов Методы неявно public. Методы могут быть public, protected, private и т.д.
Цель Определение контракта (что объект должен уметь делать). Установка типа «способность». Предоставление частичной реализации для группы родственных классов. Шаблон с общей логикой.

Практическое правило выбора:

  • Используйте интерфейс, когда нужно определить общее поведение для возможно несвязанных классов (например, Comparable, Serializable).
  • Используйте абстрактный класс, когда у нескольких классов есть общая структура данных и часть идентичной логики, которую можно вынести в родительский класс.

Пример:

// Интерфейс определяет контракт "способность летать"
interface Flyable {
    void fly(); // Абстрактный метод
    default void checkFuel() { // Default-метод с реализацией (Java 8+)
        System.out.println("Checking fuel...");
    }
}

// Абстрактный класс предоставляет общую базу для животных
abstract class Animal {
    protected String name; // Поле с состоянием

    public Animal(String name) { // Конструктор
        this.name = name;
    }

    abstract void makeSound(); // Абстрактный метод

    public void eat() { // Полностью реализованный метод
        System.out.println(name + " is eating.");
    }
}

// Класс использует оба подхода
class Bird extends Animal implements Flyable {
    public Bird(String name) {
        super(name);
    }

    @Override
    void makeSound() {
        System.out.println("Chirp!");
    }

    @Override
    public void fly() {
        System.out.println(name + " is flying in the sky.");
    }
}

Ответ 18+ 🔞

А, вот эта вечная тема, про интерфейсы и абстрактные классы! Ну, блядь, все как будто в первый раз слышат. Сидят, глаза по пять копеек, а разницу объясни — волнение ебать, терпения ноль ебать. Ладно, слушай сюда, разжую.

Представь, что ты строишь дом. Интерфейс — это, сука, как техническое задание от заказчика, бумажка, где написано: «Хочу, чтобы тут была дверь, окно и крыша, нахуй». Абстрактный класс — это уже готовый фундамент с частью стен, на который ты этот дом будешь ставить. Понял разницу? Ну, вроде как.

Так, а теперь по пунктам, чтобы даже мартышлюшка на соседнем дереве поняла.

Интерфейс (старый, добрый, до восьмой Java):

  • Наследование: Ты можешь взять и сказать: «Я — и певец, и танцор, и художник». То есть класс может реализовать (implements) дохуя интерфейсов сразу. Распиздяйство полное, но можно.
  • Поля: Только константы. public static final какие-то. То есть, переменную туда не засунешь, только табличку «Здесь был Вася» прибить можешь.
  • Методы: Раньше — только обещания, голые абстрактные методы. «Я умею летать!» — а как летать, нихуя не написано. С восьмой джавы — охуели, добавили default и static методы с телом. То есть теперь в техзадании могут быть и готовые инструкции «Как проверить бензин».
  • Конструктор: Не, ну ты чего, блядь? Какой конструктор в техническом задании? Его нет.
  • Цель: Объявить контракт, способность. «Всё, что реализует этот интерфейс, — умеет летать». И похуй, что это: птица, самолёт или тёща на метле.

Абстрактный класс:

  • Наследование: Только один, блядь. Как отец. Нельзя быть сыном двух отцов (юридически, во всяком случае). extends только один класс.
  • Поля: А вот тут — пожалуйста! Любые поля, с любым доступом. Состояние, память, всё как у людей.
  • Методы: Может и пальцем погрозить («абстрактный метод» — сделай сам, сынок), а может и полную реализацию дать («вот, ешь уже, блядь»).
  • Конструктор: Естественно, есть! Наследники же будут рождаться, их как-то инициализировать надо.
  • Цель: Дать общую базу, шаблон для родственных объектов. Вот есть «Животное». У всех есть имя, все едят. Но звуки издают разные. Вот эту общую хуйню (имя, метод eat()) и засовываем в абстрактный класс.

Практическое правило, чтобы не ебать мозг:

  • Интерфейс — когда нужно описать умение для разных сущностей. Comparable (умеет сравниваться), Serializable (умеет в архив). Самолёт и птица — оба Flyable.
  • Абстрактный класс — когда есть семейство объектов с общей логикой. «Птица», «Рыба», «Млекопитающее» — все от Animal. У всех общие поля и часть методов.

А вот тебе живой пример, смотри:

// Интерфейс — контракт "Умею летать, нахуй"
interface Flyable {
    void fly(); // Голое обещание
    default void checkFuel() { // А это уже дефолтная реализация, Java 8+ творит чудеса
        System.out.println("Checking fuel...");
    }
}

// Абстрактный класс — база для всех зверушек
abstract class Animal {
    protected String name; // А вот и состояние, поле есть!

    public Animal(String name) { // Конструктор, мать его!
        this.name = name;
    }

    abstract void makeSound(); // А это наследник должен сам сделать

    public void eat() { // А это уже готовенькое
        System.out.println(name + " is eating.");
    }
}

// А теперь смотрим, как это вместе ебётся
class Bird extends Animal implements Flyable {
    public Bird(String name) {
        super(name); // Вызываем папин конструктор
    }

    @Override
    void makeSound() { // Реализуем абстрактный метод от Animal
        System.out.println("Chirp!");
    }

    @Override
    public void fly() { // Реализуем метод от интерфейса Flyable
        System.out.println(name + " is flying in the sky.");
    }
}

Видишь? Птица (Bird) — это животное (extends Animal), поэтому у неё есть имя и она умеет жрать. И одновременно она — летающий объект (implements Flyable), поэтому обязана реализовать метод fly() и может пользоваться checkFuel().

Вот и вся магия. Не такой уж и пиздец, да? Главное — понять, для чего каждый инструмент. А то некоторые абстрактный класс на интерфейс пытаются заменить, получается хуй в пальто, бесполезная и неудобная хуйня.