The order of initializing an object instance is described in JLS (12.5 Creation of New Class Instances) (my translation):
When creating a new instance of a class, memory is allocated for all instance variables declared in the class and for all instance variables declared in each superclass, including all hidden variables (§8.3).
If memory cannot be allocated due to lack of free space, instance creation is interrupted with an OutOfMemoryError
. Otherwise, all instance variables of the new object, including those declared in the superclass, are initialized with default values. (§4.12.5).
Before a reference to the created object is returned, the specified constructor is executed to initialize a new object using the following algorithm:
- Assign constructor arguments to the parameter variables for invoking this constructor.
- If the constructor starts with an explicit call (§8.8.7.1) of another constructor of the same class (using
this
), then you need to calculate the arguments and execute that constructor recursively using the same 5 steps. If the execution of that constructor is interrupted (completes abruptly), then this algorithm will be interrupted for the same reasons, otherwise proceed to step 5. - If the constructor does not start with an explicit call to another constructor of the same class (using
this
), then for classes other than Object
constructor of the superclass is explicitly or implicitly called (using super
). It is necessary to calculate the arguments and execute the superclass constructor recursively using the same 5 steps. If the execution of that constructor is interrupted (completes abruptly), then this algorithm will be interrupted for the same reasons, otherwise proceed to step 4. - Execute instance initializers and instance variable initializers for this class, assigning the values of the instance variable initializers to the corresponding instance variables, from left to right in order of appearance in the source code of the class. If the execution of any initializer causes an exception, the following initializers are not processed, and this algorithm ends with the same exception. Otherwise, go to step 5.
- Run the rest of the constructor body. If execution is interrupted, this algorithm will be interrupted for the same reasons. Otherwise, the algorithm will complete normally.
Unlike C ++, Java does not set up separate rules for dispatching methods when creating a new instance of a class. If the methods overridden in the inheritors in the object being initialized are called, these overridden methods are used until the new object is fully initialized.
The class initialization order is described in JLS (12.4. Initialization of Classes and Interfaces) :
Class initialization consists of executing its static initializers and initializers for static fields (class variables) declared in a class.
Interface initialization consists of the execution of field initializers (constants) declared in the interface.
The class or interface T
will be initialized immediately before one of the following events:
T
is a class and an instance of class T
.- A call to a static method declared in
T
- Assignment is performed to a static field declared in
T
- A static field declared in
T
used and this field is not a constant (§4.12.4) (constant is a final
variable of primitive type or String
declared with an initializer that is a constant expression) T
is the top-level class (§7.6) and the expression assert
(assert statement) (§14.10) is lexically located inside T
(§8.1.3).
When a class is initialized, its superclasses are initialized (if they have not been initialized before); Superinterfaces are also initialized (§8.1.5) if they declare default methods (§9.4.3) (if they have not been initialized). The initialization of the interface does not in itself cause the initialization of its superinterfaces.
A static
field access (§8.3.1.1) causes initialization only for the class or interface that declared the field, even if the call was made using the name of a subclass, subinterface, or class that implements the interface.
Calling some methods from the Class Class
and the java.lang.reflect
also cause initialization of the class or interface.
The class or interface will not be initialized under other circumstances.
The goal is for initializers of a class or interface to translate it into a consistent state, and this state is the first state to be seen from other classes. Static initializers and initializers of class variables are executed in order of appearance in the text, and cannot refer to class variables declared in the text after use, despite the fact that these class variables are in scope (§8.3.3). This restriction is designed to detect cyclic or other incorrect initialization at the compilation stage.
Armed with this clumsy translation, let's see what happens in your example:
import static net.mindview.util.Print.*; class Insect { private int i = 9; protected int j; Insect() { print("i = " + i + ", j = " + j); j = 39; } private static int x1 = printInit("static Insect.x1 initialized"); static int printInit(String s) { print(s); return 47; } } public class Beetle extends Insect { private int k = printInit("Beetle.k initialized"); public Beetle() { print("k = " + k); print("j = " + j); } private static int x2 = printInit("static Beetle.x2 initialized"); public static void main(String[] args) { print("Beetle constructor"); Beetle b = new Beetle(); } }
OUTPUT:
// Вызывается статический метод класса Beetle.main // Начинается инициализация класса Beetle // Инициализируется суперкласс Insect // Вызывается инициализатор для Insect.x1 static Insect.x1 initialized // Вызывается инициализатор для Beetle.x2 static Beetle.x2 initialized // Класс Beetle инициализирован, выполняется Beetle.main // выполняется print Beetle constructor // начинается создание экземпляра конструктором Beetle() // вызов this(..) в первой строке конструктора отсутствует (Beetle. шаг 2) // . будет вызван super() (Beetle. шаг 3) т.е. Insect() // внутри Insect() аналогично вызван Object() (Insect. шаг 3) // выполнение инициализации полей (Insect. шаг4) в порядке // . появления в тексте // i = 9; (поле i) // продолжаем выполнение конструктора Insect() (Insect. шаг 5) // Вывод полей в конструкторе: j инициализирован // . значением по-умолчанию на этапе выделения памяти i = 9, j = 0 // j = 39 // тело конструктора Insect() выполнено без ошибок // возвращаемя в Beetle(), переходим к инициализации полей (Beetle. шаг 4) // k = printInit("Beetle.k initialized") Beetle.k initialized // продолжаем выполнение конструктора Beetle() (Beetle. шаг 5) k = 47 j = 39 // тело конструктора Beetle() выполнено без ошибок