Содержание

Порядок инициализации простого объекта наследника 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());
}
}
}
