
Порядок инициализации простого объекта наследника Object
Начнем с простого: в каком порядке инициализируется объект (в нашем первом случае — наследний Object, простенький такой)?
Проведем эксперемент при помощи следующего кода:
public static void main(String[] args) { new A(); }public static int fooInit(String who) { System.out.println(String.format("%s initialized", who)); return 0; }
public static void print(String what) { System.out.println(what); }
public static class A { private int instanceFieldA = fooInit("Class A instance field"); private static int staticFieldA = fooInit("Class A static(class) field"); //начало экземплярного инициализационного блока { print("Initialization block of A is called"); } //начало статического инциализационного блока static { print("Static initialization block of A is called"); }
public A() { print("Class A constructor called"); }
}
У нас в наличии: статический инициализационный блок, нестатический (дальше я буду называть его «экземплярный») инициализационный блок, статические поле и экземплярное поле. Дополняет все это дело конструктор без параметров.
Теперь, перед тем как я напишу что же мы получим в консоли, я предлагаю вам выдвинуть свои версии.
Выдвинули? А теперь версия JVM
Class A static(class) field initialized
Static initialization block of A is called
Class A instance field initialized
Initialization block of A is called
Class A constructor called
Делаем выводы (пока первая итерация):
- Первыми инициализируются (вызываются) статические элементы класса
- После статических иниализируются (вызываются) экземплярные поля и блоки
- Самым последним отрабатывает конструктор
Окей, теперь давайте поменяем порядок объявления членов класса, а точнее — поменяем поставим статический блок раньше статического поля. Вот так:
static { print("Static initialization block of A is called"); } private static int staticFieldA = fooInit("Class A static(class) field");
На что мы получим:
Static initialization block of A is called
Class A static(class) field initialized
Class A instance field initialized
Initialization block of A is called
Class A constructor called
И из этого сделаем выводы (а точнее — дополним ранее сделанные выводы), что:
- Статические поля (блоки инициализации) инициализируются (выполняются) в порядке их объявления
- Экземплярные поля (блоки инициализации) инициализируются (выполняются) в порядке их объявления
Теперь исследуем поведение статических членов класса, для этого изменим код метода main()
следующим образом:
public static void main(String[] args) { new A(); print("-----"); new A(); }
На что консоль ответит так:
Class A static(class) field initialized
Static initialization block of A is called
Class A instance field initialized
Initialization block of A is called
Class A constructor called
-----
Class A instance field initialized
Initialization block of A is called
Class A constructor called
Как вы можете видеть, второй раз статической инициализации не было.
А это потому, что выполнение статического кода происходит только один раз и во время первой загрузки класса.
Инициализация иерархии объектов
Выше мы рассматривали ситуацию, когда наследование происходит от Object, это простой случай. Теперь перейдем к чуть более сложному и создадим иерархию объектов.
Добавим к классу A дочерник класс B (вот такие оригинальные имена классов ):
public static class B extends A { private int instanceFieldB = fooInit("Class B instance field"); private static int staticFieldB = fooInit("Class B static(class) field");{ print("Initialization block of B is called"); }
static { print("Static initialization block of B is called"); }
public B() { print("Class B constructor called"); }
}
Суть таже: во время инициализации класса в консоли появляются сообщения. Метод main() приведем к виду:
public static void main(String[] args) { new B(); }
Запускаем это дело и видим:
Class A static(class) field initialized
Static initialization block of A is called
Class B static(class) field initialized
Static initialization block of B is called
Class A instance field initialized
Initialization block of A is called
Class A constructor called
Class B instance field initialized
Initialization block of B is called
Class B constructor called
Сколько всего! А ведь мы только один класс добавили в иерархию. Давайте делать выводы:
- Инициализационные блоки наследуются — что явно видно из примера. Поэтому с ними нужно быть осторожными, так как переопределить их в потомках не получится.
- Статические члены классов инициализируются в порядке «от родителя к наследнику» самыми первыми (при первой загрузке/обращении к классу).
- После инициализации статических членов начинается экземплярная инициализации. Порядок ее таков: все нестатические члены родителя, конструктор родителя, все нестатические члены наследника, конструктор наследника.
Надеюсь, вы разобрались с инициализацией. Самый лучший способ все это запомнить — это повторить мой экперимент. Его вариаций огромное множесто, так что дерзайте!)
Вопрос вместо заключения
Так как вы уже человек просвященный в вопросах инициализации, то я хочу напоследок предложить эксперимент (правда он затрагивает наследование, но не думаю что вас это остановит).
Внимание вопрос: каков будет результат выполнения нижеприведенного кода? А самый главный вопрос «почему он будет таким?»
public class TestYourSelf {public static void main(String[] args) { new Child(); }
static class Parent { Parent() { foo(); }
void foo() { //empty method } }
static class Child extends Parent { private String x = "hello-buddy";
@Override void foo() { System.out.println(x.length()); } }
}