Ключевое слово super в Java - Вопросы
Всего: 6 вопросов
1. Что такое ключевое слово super в Java и какие у него есть формы?
Что такое ключевое слово super в Java и какие у него есть формы?
super — это ссылка на непосредственный суперкласс (родительский класс) текущего объекта, то есть на ту часть объекта, которая пришла от предка. У ключевого слова две формы:
1. Вызов конструктора суперкласса — super(списокАргументов);. Допустим только внутри конструктора и только один раз.
2. Обращение к члену суперкласса — super.member: к полю, скрытому одноимённым полем наследника, или к методу, переопределённому в наследнике.
Важно: super — не объект и не переменная. Записать Object o = super; нельзя: это специальная форма доступа, которую компилятор разворачивает в невиртуальный вызов (инструкция invokespecial в байт-коде). В статическом методе и статическом блоке super недоступен, потому что объекта ещё нет.
2. Почему код перестаёт компилироваться с ошибкой there is no default constructor available и кто подставляет неявный super()?
Почему код перестаёт компилироваться с ошибкой there is no default constructor available и кто подставляет неявный super()?
Если в конструкторе наследника нет явного вызова super(...) или this(...), компилятор (не JVM) сам подставляет super() без аргументов первой строкой:
public HeavyBox() {
super(); // подставлено компилятором
this.weight = -1;
}
Как только в суперклассе объявлен хотя бы один конструктор с параметрами, конструктор по умолчанию перестаёт генерироваться. Неявному super() становится не к чему привязаться — и сборка падает с сообщением there is no default constructor available in Box, причём в файлах наследников, которые вы не трогали.
Два способа починить: добавить в родителя конструктор без параметров либо явно вызвать существующий конструктор — super(width, height, depth);.
3. В каком порядке выполняются конструкторы при создании объекта в многоуровневой иерархии классов?
В каком порядке выполняются конструкторы при создании объекта в многоуровневой иерархии классов?
Тела конструкторов выполняются от корня иерархии к текущему классу. Для иерархии SuperSuperClass → SuperClass → SomeClass вызов new SomeClass() даёт вывод:
В конструкторе SuperSuperClass
В конструкторе SuperClass
В конструкторе SomeClass
Механика такая: каждый конструктор сначала передаёт управление наверх по цепочке неявных или явных super() — до самого java.lang.Object, — и только на обратном пути выполняются тела, сверху вниз, от предка к потомку. Смысл в том, что к моменту старта тела конструктора наследника унаследованная часть объекта уже полностью проинициализирована.
Обратный порядок в логах — классическая ловушка на собеседовании.
4. Зачем нужна форма super.member и почему super.print() внутри переопределённого метода print() не вызывает бесконечную рекурсию?
Зачем нужна форма super.member и почему super.print() внутри переопределённого метода print() не вызывает бесконечную рекурсию?
super.member нужен только тогда, когда имя перекрыто в наследнике: в остальных случаях унаследованные члены и так видны по простому имени.
public class D extends C {
public String i; // скрывает int i из класса C
public D(String a, int b) {
i = a;
super.i = b; // единственный способ добраться до int i
}
public void print() {
System.out.println("D.i = " + i);
super.print(); // вызов реализации класса C
}
}
Поля скрываются (hiding) и разрешаются по типу ссылки, а методы переопределяются (overriding) и разрешаются по реальному типу объекта. Обычный вызов print() внутри D.print() ушёл бы обратно в D.print() — то есть в бесконечную рекурсию. super.print() компилируется в невиртуальный вызов (invokespecial), который жёстко привязан к реализации суперкласса, поэтому рекурсии не возникает.
Форма super.super.method() в Java запрещена: перепрыгнуть через уровень иерархии нельзя. Для default-метода интерфейса используется квалифицированная форма ИмяИнтерфейса.super.method(), например Walkable.super.move().
5. Чем отличаются super(...), this(...), super.member и this.member? Можно ли вызвать super() и this() в одном конструкторе?
Чем отличаются super(...), this(...), super.member и this.member? Можно ли вызвать super() и this() в одном конструкторе?
super(...) вызывает конструктор суперкласса, this(...) — другой конструктор этого же класса. Обе конструкции допустимы только внутри конструктора и только один раз.
super.member обращается к скрытому полю или переопределённому методу предка, this.member — к полю или методу текущего объекта. Эти две формы доступны в любом методе экземпляра и в конструкторе.
Нет, super(...) и this(...) взаимоисключающие — в одном конструкторе допускается ровно один явный вызов. Если конструктор начинается с this(...), компилятор не подставляет неявный super(): конструктор суперкласса отработает в том конструкторе, которому вы делегировали работу. В итоге на один создаваемый объект приходится ровно один вызов super(...).
6. Что такое Flexible Constructor Bodies, с какой версии Java можно писать код перед super() и что запрещено делать в прологе конструктора?
Что такое Flexible Constructor Bodies, с какой версии Java можно писать код перед super() и что запрещено делать в прологе конструктора?
Исторически super() или this() обязаны были быть первым оператором конструктора. Ограничение снимали постепенно: JEP 447 (Java 22, preview) → JEP 482 (Java 23) → JEP 492 (Java 24) → JEP 513 (Java 25) — финальная фича, флаг --enable-preview не нужен. На LTS-версиях Java 17 и Java 21 действует старое правило.
Тело конструктора делится на пролог (код до super()/this()) и эпилог (код после). Главное правило пролога: обращаться к создаваемому объекту ещё нельзя. В прологе можно присваивать значения полям текущего класса, вызывать static-методы и выполнять любые локальные вычисления, включая проверки, циклы и throw. Нельзя читать поля, трогать унаследованные поля, вызывать методы экземпляра и передавать this куда-либо.
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;
}
Пишете под Java 17/21 — выносите подготовку аргументов в приватный static-метод и вызывайте его прямо в super(...).