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

Ключевое слово super в Java

Вы добавили в родительский класс Box конструктор с параметрами — и проект перестал собираться: constructor Box in class Box cannot be applied to given types. При этом в классах-наследниках вы не тронули ни одной строки. Виноват невидимый вызов super(), который компилятор сам дописывает первой строкой каждого конструктора.

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

1. Что такое ключевое слово super

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

Суперкласс (superclass, родительский класс, предок) — это класс, от которого наследуется текущий класс. Если написано class HeavyBox extends Box, то Box — суперкласс, а HeavyBox — подкласс (наследник).

У ключевого слова super есть две формы:

  1. Вызов конструктора суперкласса. После super ставятся круглые скобки, в которых передаётся список аргументов:
    super(списокАргументов);
  2. Обращение к полю или методу суперкласса, который скрыт или переопределён в подклассе:
    super.member;

2. Вызов конструктора суперкласса через super()

Передача параметров в конструктор суперкласса

Если в иерархии классов требуется передать параметры конструктору суперкласса, то все подклассы должны передавать эти параметры вверх по иерархии. То есть из конструктора подкласса нужно вызвать конструктор суперкласса с помощью super().

В этом примере из конструктора класса HeavyBox вызывается конструктор класса Box с помощью super(), тем самым передаются необходимые значения:

public class Box {
    double width;
    double height;
    double depth;

    Box(double w, double h, double d) {
        width = w;
        height = h;
        depth = d;
    }

    public Box() {
    }
}
public class HeavyBox extends Box {
    int weight;

    public HeavyBox(int width, int height, int depth, int weight) {
        super(width, height, depth);
        this.weight = weight;
    }

    public HeavyBox() {
        this.weight = -1;
    }
}

Если в конструкторе наследника нет явного вызова super() — как во втором конструкторе класса HeavyBox — компилятор сам подставляет его первой строкой:

public HeavyBox() {
    super();          // подставлено компилятором
    this.weight = -1;
}

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

Важно

Неявный super() добавляет компилятор, а не JVM — поэтому проблема всплывает на этапе сборки. Если в классе Box удалить конструктор без параметров, компилятор сообщит: there is no default constructor available in Box. Как только вы объявили в классе хотя бы один конструктор с параметрами, конструктор по умолчанию перестаёт генерироваться автоматически.

Порядок вызова конструкторов в многоуровневой иерархии

Когда super() вызывается из подкласса, выполняется конструктор его непосредственного суперкласса. Это справедливо и для многоуровневой иерархии. Допустим, в иерархии есть три класса:

Иерархия классов SuperSuperClass, SuperClass, SomeClass в Java

Есть SuperSuperClass, его расширяет SuperClass, а SomeClass расширяет SuperClass. Код этих классов предельно короткий — каждый конструктор просто печатает своё имя:

public class SuperSuperClass {
    public SuperSuperClass() {
        System.out.println("В конструкторе SuperSuperClass");
    }
}

public class SuperClass extends SuperSuperClass {
    public SuperClass() {
        System.out.println("В конструкторе SuperClass");
    }
}

public class SomeClass extends SuperClass {
    public SomeClass() {
        System.out.println("В конструкторе SomeClass");
    }
}

Создадим объект класса SomeClass:

SomeClass someClass = new SomeClass();

Вывод в консоль:

В конструкторе SuperSuperClass
В конструкторе SuperClass
В конструкторе SomeClass

Хотя создаётся объект SomeClass, первым отрабатывает конструктор SuperSuperClass, затем SuperClass и только потом SomeClass. Причина в цепочке неявных super(): каждый конструктор сначала передаёт управление наверх, до самого корня иерархии (в конечном счёте до java.lang.Object), а тела конструкторов выполняются уже на обратном пути — сверху вниз, от предка к потомку. Логика простая: к моменту, когда выполняется тело конструктора наследника, унаследованная часть объекта уже полностью проинициализирована.

3. Код перед super(): Flexible Constructor Bodies

Исторически вызов super() обязан был быть первым оператором в конструкторе. Такой код до Java 22 не компилировался:

public class SomeClass extends SuperClass {
    public SomeClass() {
        System.out.println("В конструкторе SomeClass"); // ошибка компиляции до Java 22
        super();
    }
}

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

Постепенно ограничение сняли:

Версия Java JEP Название Статус
Java 22 JEP 447 Statements before super(...) Preview (нужен --enable-preview)
Java 23 JEP 482 Flexible Constructor Bodies Second preview
Java 24 JEP 492 Flexible Constructor Bodies Third preview
Java 25 JEP 513 Flexible Constructor Bodies Финальная фича, флаг не нужен

Что изменилось

Начиная с Java 25 Flexible Constructor Bodies — обычная часть языка. Но на LTS-версиях Java 17 и Java 21, которые до сих пор стоят в большинстве продакшен-проектов, правило прежнее: super() или this() должны быть первой строкой конструктора. Пишете под Java 17/21 — выносите подготовку аргументов в приватный static-метод и вызывайте его прямо в super(...).

Пролог и эпилог конструктора

Тело конструктора делится на две части:

  • пролог — код до вызова super() или this();
  • эпилог — код после него (либо всё тело, если явного вызова нет).

Главное правило пролога: в нём ещё нельзя обращаться к создаваемому объекту, потому что он не готов.

В прологе Можно Нельзя
Поля текущего класса Присваивать значение (this.x = 1;) Читать значение
Унаследованные поля Ни читать, ни писать
Методы Вызывать static-методы Вызывать методы экземпляра
Ссылка this Только слева от = при присваивании полю Передавать как значение, возвращать
Внутренние классы Обращаться к членам внешнего (outer) класса Создавать нестатические inner-классы
Локальные вычисления Любые: проверки, циклы, throw

Сценарий 1. Валидация аргументов

Вернёмся к классу HeavyBox. Нужно проверить, что ширина больше нуля: если она меньше либо равна нулю, создавать объект бессмысленно — выбрасываем исключение до вызова конструктора родителя.

public HeavyBox(int width, int height, int depth, int weight) {
    if (width <= 0) {
        throw new IllegalArgumentException("width must be positive");
    }
    super(width, height, depth);
    this.weight = weight;
}

Сценарий 2. Подготовка аргументов

Допустим, размеры нужно удвоить перед передачей в конструктор суперкласса:

public HeavyBox(int width, int height, int depth, int weight) {
    width *= 2;
    height *= 2;
    depth *= 2;
    super(width, height, depth);
    this.weight = weight;
}

Сценарий 3. Переиспользование одного объекта в аргументах

Пусть в классе Box есть конструктор, принимающий два объекта Point:

public class Box {
    // ...
    public Box(Point p1, Point p2) {
    }
    // ...
}

Если перед super() нельзя писать код, придётся создать два разных объекта (или принимать их извне):

public HeavyBox() {
    super(new Point(), new Point());
}

С гибким телом конструктора достаточно создать один объект и передать его дважды:

public HeavyBox() {
    Point p = new Point();
    super(p, p);
}

4. Обращение к полям и методам суперкласса

Вторая форма — super.member — даёт доступ к полю или методу суперкласса из класса-наследника. В большинстве случаев super не нужен: унаследованные члены и так видны по имени. Он необходим только тогда, когда имя перекрыто в подклассе.

В классе C объявлено поле i типа int. В его наследнике D тоже объявлено поле i, но типа String. (Сразу предупредим: на практике так делать не стоит — пример нужен только для иллюстрации.) Из класса D по имени i доступно только поле String i, которое скрывает int i. Чтобы добраться до int i, нужен super.i.

С методами похоже. В обоих классах определён метод print(). Чтобы из D вызвать реализацию класса C, пишем super.print().

public class C {
    public int i;

    public void print() {
        System.out.println("C.i = " + i);
    }
}

public class D extends C {
    public String i;

    public D(String a, int b) {
        i = a;
        super.i = b;
    }

    public void print() {
        System.out.println("D.i = " + i);
        super.print();
    }
}

public class UseSuperExample {
    public static void main(String[] args) {
        D d = new D("someString", 2);
        d.print();
        System.out.println(d.i);
    }
}

Вывод в консоль:

D.i = someString
C.i = 2
someString

Обратите внимание на разницу между полями и методами. Поля скрываются (hiding) и разрешаются по типу ссылки, а методы переопределяются (overriding) и разрешаются по реальному типу объекта. Именно поэтому super.print() — единственный способ вызвать «родительскую» версию метода из переопределённой: обычный вызов print() внутри D привёл бы к бесконечной рекурсии.

Запомните

super — это не объект и не ссылка, которую можно сохранить в переменную: написать Object o = super; нельзя. Это специальная форма доступа, которую компилятор разворачивает в невиртуальный вызов (инструкция invokespecial в байт-коде) — именно поэтому super.print() не приводит к рекурсии.

5. super, super(), this и this(): в чём разница

Четыре похожие конструкции, которые часто путают на собеседовании:

Конструкция Что делает Где можно использовать Типичный пример
super(...) Вызывает конструктор суперкласса Только в конструкторе, один раз super(width, height, depth);
this(...) Вызывает другой конструктор этого же класса Только в конструкторе, один раз this(0, 0, 0, 0);
super.member Обращается к скрытому полю или переопределённому методу предка В любом методе экземпляра и в конструкторе super.print();
this.member Обращается к полю или методу текущего объекта В любом методе экземпляра и в конструкторе this.weight = weight;

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

6. Где чаще всего спотыкаются

  • Забыли конструктор без параметров у родителя. Добавили в суперкласс конструктор с аргументами — и все наследники, полагавшиеся на неявный super(), перестали компилироваться.
  • super в статическом контексте. В static-методе и в статическом блоке super (как и this) недоступен: объекта ещё нет.
  • Вызов переопределяемого метода из конструктора. Если конструктор суперкласса вызывает метод, переопределённый в наследнике, тот отработает до инициализации полей наследника и увидит null или 0.
  • Попытка написать super.super.method(). Такой синтаксис в Java запрещён: «перепрыгнуть» через уровень иерархии нельзя.
  • Порядок вывода в логах. Тела конструкторов выполняются от корня иерархии к листу, а не наоборот — классический вопрос на собеседовании.

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

Можно ли вызвать super() и this() в одном конструкторе?

Нет. В конструкторе допускается ровно один явный вызов — либо super(...), либо this(...). Если написан this(...), компилятор не подставляет неявный super(): конструктор суперкласса будет вызван в том конструкторе, которому вы делегировали работу. В итоге в цепочке всё равно происходит ровно один вызов super(...).

Почему не работает super.super.method()?

Такой синтаксис запрещён спецификацией Java. Пропустить уровень иерархии нельзя: класс отвечает только за контракт со своим непосредственным родителем, и возможность обойти его реализацию нарушила бы инкапсуляцию. Если доступ к методу «через уровень» действительно нужен, добавьте в промежуточном классе метод-мост, который вызывает super.method().

Как вызвать default-метод интерфейса, если класс его переопределил?

Через квалифицированную форму ИмяИнтерфейса.super.method(), например Walkable.super.move(). Это работает только для интерфейсов, которые класс реализует напрямую, и особенно полезно, когда два интерфейса дают конфликтующие реализации одного default-метода.

Можно ли использовать super в статическом методе?

Нет. Ключевые слова super и this связаны с конкретным экземпляром, а статический метод выполняется без объекта, поэтому такой код не скомпилируется. Чтобы обратиться к статическому члену родителя, используйте имя класса: Box.someStaticMethod().

Что означает ошибка there is no default constructor available?

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

Итоги

  • super ссылается на непосредственный суперкласс: super(...) вызывает его конструктор, super.member даёт доступ к его полю или методу.
  • Если явного вызова нет, компилятор сам подставляет super() первой строкой — значит, у родителя должен быть конструктор без параметров.
  • Тела конструкторов выполняются от корня иерархии к текущему классу, а не наоборот.
  • super.member нужен только тогда, когда имя скрыто или переопределено в наследнике.
  • Код перед super() разрешён начиная с Java 25 (JEP 513); на Java 17 и 21 вызов конструктора суперкласса по-прежнему должен быть первым оператором.

Презентацию с видео можно скачать на Patreon.

Презентация на Patreon →

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

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

Комментарии

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