Наследование в Java и ключевое слово extends - Вопросы

Всего: 6 вопросов

1. 

Что такое наследование в Java и как оно объявляется через extends?

Наследование — это механизм ООП, при котором один класс (подкласс) получает поля и методы другого класса (суперкласса) и может добавлять к ним собственные. Объявляется ключевым словом extends: class ColorBox extends Box6. Класс может расширять ровно один суперкласс, но у одного суперкласса может быть сколько угодно наследников. Наследование задаёт отношение IS-A («является»): ColorBox является Box6. Если фраза «X является Y» звучит натянуто, вместо наследования нужна композиция (отношение HAS-A): объект хранит другой объект в поле и делегирует ему работу. На вершине любой иерархии стоит java.lang.Object — класс без extends наследует его неявно.

2. 

Что подкласс наследует от суперкласса, а что нет?

Наследуются: public- и protected-поля и методы (доступны в подклассе по имени, как собственные); static-поля и методы — но переопределить их нельзя, одноимённый static-метод лишь скрывает родительский. Члены без модификатора (package-private) видны наследнику только внутри того же пакета.

Не наследуются: private-поля и методы — они физически существуют в объекте подкласса, но обратиться к ним по имени нельзя, только через public- или protected-метод суперкласса; конструкторы — конструктор родителя не становится конструктором наследника, он лишь вызывается из него через super(...).

3. 

Чем upcasting отличается от downcasting и когда возникает ClassCastException?

Upcasting — переменная типа суперкласса ссылается на объект подкласса: Box6 box = new HeavyBox(15, 10, 20, 5);. Выполняется автоматически и всегда безопасен, потому что каждый HeavyBox является Box6. Именно на upcasting опирается полиморфизм.

Downcasting — обратное, явное приведение к типу подкласса: HeavyBox heavy = (HeavyBox) box;. Оно нужно, чтобы добраться до членов подкласса, потому что набор доступных членов определяет тип ссылки, а не тип объекта. Компилятор такое приведение пропускает всегда, а в рантайме, если объект на самом деле не является HeavyBox, выбрасывается ClassCastException. Защита — проверка instanceof, а с Java 16 — pattern matching: if (box instanceof HeavyBox heavy) { ... }.

Исключение из правила про тип ссылки: переопределённый метод всегда вызывается по фактическому типу объекта (динамическая диспетчеризация).

4. 

В каком порядке вызываются конструкторы в иерархии наследования и почему?

Конструкторы выполняются сверху вниз — от корня иерархии к самому дальнему потомку. Для цепочки EFG вызов new G() напечатает E, затем F, затем G.

Причина в том, что первой инструкцией конструктора подкласса компилятор неявно подставляет вызов конструктора суперкласса без аргументов. Подкласс достраивает объект поверх состояния родителя, поэтому родительская часть должна быть проинициализирована первой.

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

5. 

Почему в Java нет множественного наследования классов и чем его заменяют?

Класс в Java может расширять только один суперкласс: запись class Shipment extends HeavyBox, ColorBox {} не компилируется. Причина — проблема ромба (diamond problem): если два родителя содержат одноимённое поле или метод, компилятор не может решить, какую версию унаследовать.

Java запретила множественное наследование состояния и реализации, но оставила множественное наследование типов через интерфейсы: class Shipment extends HeavyBox implements Comparable<Shipment>, Serializable. После extends — ровно один класс, после implements — сколько угодно интерфейсов.

С Java 8 интерфейсы могут содержать default-методы с реализацией, поэтому конфликт снова возможен. Компилятор его не терпит: если два интерфейса дают одинаковый default-метод, класс обязан переопределить его сам и при необходимости выбрать нужную версию через Имя.super.method().

6. 

Как запретить наследование класса в Java: final и sealed

Есть три уровня контроля:

final class — класс нельзя наследовать вообще. Так объявлены String, Integer, LocalDate: это гарантирует, что никто не сломает инварианты неизменяемого объекта.

final-метод — сам класс наследовать можно, но конкретный метод переопределить нельзя.

sealed class (Java 17) — список наследников фиксирован автором: public sealed class Box permits ColorBox, HeavyBox {}. Каждый разрешённый наследник обязан быть объявлен как final, sealed или non-sealed. Иерархия целиком известна компилятору, поэтому switch по типу можно проверить на исчерпывающность.

Страница 1 из 1