Разберите порядок инициализации и полиморфизм в Java на примере: class Parent { Parent(){ init(); } void init(){ System.out.println("Parent"); } } class Child extends Parent { int x = 5; void init(){ System.out.println(x); } } — почему вывод может отличаться от ожидаемого и как это влияет на дизайн конструкторов

4 Мая в 06:00
12 +1
0
Ответы
1
Коротко: Parent-конструктор вызывает виртуальный метод `init()`. При создании `new Child()` вызывается `Parent()` до того, как полям `Child` присвоены явные значения, поэтому вызов виртуального `Child.init()` происходит, когда `x` ещё имеет значение по умолчанию — не 555, а 000. В результате на экран может быть напечатано 000, а не ожидаемое 555.
Порядок инициализации (при `new Child()`):
- Выделяется память под объект; все поля примитивных типов получают значения по умолчанию (для `int` это 000), ссылочные — `null`.
- Выполняется цепочка конструкторов: сначала `Parent()` (непосредственно вызывается `super()`).
- Тело `Parent()` выполняется; в нём вызывается `init()` — динамическая диспетчеризация вызывает `Child.init()`, но поля `Child` ещё не инициализированы явными инициализаторами (они всё ещё 000/`null`).
- После возврата из `Parent()` выполняются инициализаторы полей и блоки инициализации `Child` (тогда `x` присваивается 555).
- Затем выполняется тело конструктора `Child()`.
Почему это важно для дизайна:
- Нельзя безопасно вызывать переопределяемые (не `final`, не `private`) методы из конструкторов суперкласса, потому что переопределённый код увидит ещё неинициализированное состояние подкласса.
- Рекомендации:
- Не вызывать overridable-методы из конструкторов суперклассов.
- Сделать такие методы `private` или `final`, если они используются в конструкторе суперкласса.
- Перенести инициализацию, зависящую от подкласса, в конструктор подкласса или в фабричный метод (static factory) после полной инициализации объекта.
- Либо передавать нужные значения через параметры конструктора суперкласса (поддерживая инварианты до вызова `super(...)`).
Пример безопасной правки: либо вызвать `init()` в конструкторе `Child` после установки полей, либо сделать `Parent.init()` `final`/`private`, чтобы не было динамического вызова до инициализации полей.
4 Мая в 06:08
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир