ООП ·
‹ Предыдущий Следующий ›
⏱ 5 минут чтения Обновлено: 2026-08-08

Абстрактные классы и методы в Java

Строка Figure figure = new Figure(10, 10); выглядит безобидно, но компилятор отвечает Figure is abstract; cannot be instantiated. Одно слово abstract в объявлении класса навсегда запрещает создавать его объекты — и это не ограничение, а главный смысл конструкции: класс описывает что умеют наследники, не говоря как.

Абстрактный класс в Java — это класс, объявленный с модификатором abstract, объект которого создать нельзя; он служит общим предком для подклассов. Абстрактный метод — это метод без реализации: у него есть сигнатура, но нет тела. Само слово abstract переводится как «абстрактный, отвлечённый» — отвлечённый от конкретной реализации.

Абстрактный метод: синтаксис и правила

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

Общая форма:

abstract тип имяМетода(списокПараметров);

Пример:

public abstract double calculateArea();

Правила абстрактного метода:

  • у него нет тела — только сигнатура и точка с запятой;
  • он должен быть реализован в первом неабстрактном наследнике (промежуточный наследник может остаться абстрактным и передать обязанность дальше);
  • объявить его можно только внутри абстрактного класса или интерфейса;
  • он не может быть одновременно static, final или private.

Абстрактный класс: объявление и объекты

Любой класс, содержащий хотя бы один абстрактный метод, обязан сам быть объявлен абстрактным. Для этого достаточно указать ключевое слово abstract перед словом class:

public abstract class Figure {
    // ...
    public abstract double calculateArea();
}

У абстрактного класса не может быть собственных объектов, но его тип можно использовать для ссылок на объекты наследников:

public class Demo {
    public static void main(String[] args) {
        // Figure figure = new Figure(10, 10); // ошибка компиляции:
        // Figure is abstract; cannot be instantiated

        Figure figure = new Rectangle(10, 20); // так можно: ссылка типа Figure
        System.out.println(figure.calculateArea());
    }
}

Именно ради этой возможности абстракция и нужна: переменная, параметр метода или элемент коллекции объявляются общим абстрактным типом, а конкретный объект подставляется во время выполнения. Это базовый приём абстракции в ООП — работать с сущностью через её обобщённый контракт, не зная реализации.

Важно

Обратное утверждение неверно: класс может быть абстрактным без единого абстрактного метода. Такой класс тоже нельзя инстанцировать — это законный способ сказать «пользуйтесь наследниками, а не мной». В JDK так сделаны, например, java.util.AbstractList и java.util.Calendar.

Пример: Figure, Rectangle и Triangle

Возьмём классы Figure, Triangle и Rectangle из урока «Переопределение методов». Там Figure был обычным классом и возвращал бессмысленный ноль площади. Класс описывает абстрактную фигуру, у которой площади не существует, поэтому calculateArea() правильнее сделать абстрактным, а сам класс — абстрактным.

Абстрактный класс Figure и его наследники Rectangle и Triangle в Java

public abstract class Figure {
    double dimension1;
    double dimension2;

    public Figure(double dimension1, double dimension2) {
        this.dimension1 = dimension1;
        this.dimension2 = dimension2;
    }

    public abstract double calculateArea();
}

Обратите внимание: у абстрактного класса есть поля и конструктор, хотя объектов у него не бывает. Конструктор нужен наследникам — они вызывают его через super(...), чтобы инициализировать унаследованную часть состояния.

Любой подкласс, производный от абстрактного класса, обязан реализовать все абстрактные методы суперкласса либо сам быть объявлен абстрактным:

public class Rectangle extends Figure {
    public Rectangle(double dimension1, double dimension2) {
        super(dimension1, dimension2);
    }

    @Override
    public double calculateArea() {
        System.out.println("В области четырёхугольника.");
        return dimension1 * dimension2;
    }
}
public class Triangle extends Figure {
    public Triangle(double dimension1, double dimension2) {
        super(dimension1, dimension2);
    }

    @Override
    public double calculateArea() {
        System.out.println("В области треугольника.");
        return dimension1 * dimension2 / 2;
    }
}

Совет

Ставьте @Override над реализацией абстрактного метода. Если вы ошибётесь в имени или списке параметров, компилятор сразу скажет об этом, а без аннотации получится новый метод, и класс останется абстрактным по факту — с невнятной ошибкой в другом месте файла.

Массив абстрактного типа и полиморфизм

Объектов у абстрактного класса нет, а вот массив (или List) абстрактного типа создать можно: он хранит ссылки на объекты наследников.

public class FindAreasExample {
    public static void main(String[] args) {
        Figure[] figures = new Figure[3];

        // figures[0] = new Figure(10, 10); // не скомпилируется: Figure абстрактный

        figures[0] = new Rectangle(10, 10);
        figures[1] = new Rectangle(20, 10);
        figures[2] = new Triangle(10, 10);

        for (Figure figure : figures) {
            double area = figure.calculateArea();
            System.out.println(area);
        }
    }
}

Результат выполнения программы:

В области четырёхугольника.
100.0
В области четырёхугольника.
200.0
В области треугольника.
50.0

Компилятор видит только тип Figure и разрешает вызов, потому что метод объявлен в абстрактном классе. Какая именно реализация выполнится, решает JVM во время работы программы по реальному типу объекта — это динамическое связывание.

Что можно и что нельзя в абстрактном классе

Элемент Разрешён? Пояснение
Абстрактные методы Да Ноль, один или несколько — ограничений нет
Обычные методы с телом Да Готовое поведение, общее для всех наследников
Поля, в том числе не-final Да Обычное состояние; в интерфейсе так нельзя
Конструктор Да Вызывается из наследника через super(...)
Статические методы и блоки Да Но статический метод не может быть абстрактным
Создание объекта new Нет cannot be instantiated; исключение — анонимный подкласс
abstract + final Нет Противоречие: final запрещает наследование, а без него абстрактный класс бесполезен
abstract + static у метода Нет Статические методы не переопределяются, реализацию подставить некому
abstract + private у метода Нет private не виден наследнику, реализовать его невозможно
Абстрактный конструктор Нет Конструкторы не наследуются и не переопределяются

Абстрактный класс или интерфейс

После появления default-методов в Java 8 граница между интерфейсом и абстрактным классом стала тоньше, но не исчезла.

Критерий Абстрактный класс Интерфейс
Сколько можно унаследовать Только один Сколько угодно
Изменяемое состояние (поля) Да, любые поля Нет, только public static final
Конструктор Есть Нет
Модификаторы доступа членов Любые, включая protected и private По умолчанию public
Когда выбирать Наследники — вариации одной сущности с общим состоянием и кодом Нужен контракт-роль для несвязанных классов

На чём чаще всего ошибаются

  • Пытаются создать объект абстрактного класса. new Figure(10, 10) не компилируется, даже если у класса есть конструктор.
  • Забывают реализовать метод в наследнике. Тогда компилятор потребует либо реализацию, либо пометить сам наследник как abstract.
  • Сужают видимость при реализации. Метод, объявленный public abstract, нельзя реализовать как protected — доступ разрешено только расширять.
  • Делают абстрактным класс без наследников. Абстракция без реализаций — это просто код, который нельзя выполнить.
  • Путают «абстрактный» с «пустым». Метод с телом { } или return 0; — это обычный метод: компилятор не заставит его переопределить, и ошибка всплывёт уже во время работы программы.

Часто задаваемые вопросы

Можно ли создать объект абстрактного класса?

Напрямую нельзя: new Figure(10, 10) даёт ошибку компиляции. Но можно создать объект анонимного подкласса — запись new Figure(10, 10) { public double calculateArea() { return 0; } } компилируется, потому что здесь создаётся не сам Figure, а безымянный наследник, реализующий абстрактный метод.

Зачем абстрактному классу конструктор, если объектов у него нет?

Конструктор абстрактного класса инициализирует унаследованные поля и вызывается из конструктора наследника через super(...). Если явного вызова нет, компилятор подставит super() сам — и если конструктора без параметров у абстрактного класса не окажется, наследник не скомпилируется.

Что будет, если наследник не реализует абстрактный метод?

Компилятор выдаст ошибку вида «is not abstract and does not override abstract method». Выхода два: реализовать метод или объявить сам наследник абстрактным и переложить обязанность на следующий класс в цепочке наследования.

Может ли абстрактный класс наследоваться от обычного?

Да. Абстрактный класс может расширять любой неабстрактный класс, добавляя к нему абстрактные методы. Обратное тоже верно: обычный класс наследуется от абстрактного, если реализует все его абстрактные методы.

Абстрактный класс или интерфейс: что выбрать?

Абстрактный класс берут, когда у наследников есть общее состояние и готовый код, а сами они — разновидности одной сущности. Интерфейс берут, когда нужно описать роль или способность для не связанных между собой классов, либо когда одному классу требуется несколько таких ролей: интерфейсов можно реализовать сколько угодно, а унаследовать класс — только один.

Видео объяснение

Предпочитаете видеоформат? Посмотрите этот урок с примерами и объяснениями.

Комментарии

Evgeny May 5, 2021
123
Evgeny May 5, 2021
В последнем блоке неверный код.
May 6, 2021
Что именно не так?
unknown Sep 2, 2022
К последнему комментарию - в классе FindAreas: 1. Тип массива должен быть Figure2[], а не Figure[] 2. Строку figures[0] = new Figure(10, 10); следует закомментировать, либо указать у объекта тип подкласса. Так как Figure - абстрактный класс.
unknown Sep 5, 2022
Rectangle
unknown Sep 5, 2022
В последнем примере, код правильный но не корректный, т.к. не соответствует описанию задачи. По описанию это должен быть абстрактный класс Figure2 и его наследники Rectangle2 и Triangle2, а показан пример где класс Figure не абстрактный и Rectangle и Triangle наследуются тоже не от абстрактного классса.
unknown Sep 5, 2022
Исходя из вывода и описания задачи и логике темы урока строки создания и инициализации элементов массива должны быть примерно такие: Figure2[] figures = new Figure2[3]; figures[0] = new Rectangle2(20, 10); figures[1] = new Rectangle2(10, 10); figures[2] = new Triangle2(10, 10);

Зарегистрируйтесь или войдите, чтобы иметь возможность оставить комментарий.