Source: http://blog.csdn.net/u011080472/article/details/51330114
The order of Java class initialization is often confusing. Now this article tries to test the initialization order of Java classes in non-inheritance and inheritance relationships from the perspective of JVM, and try to give an explanation from the perspective of JVM.
Initialization order in non-inheritance relationships
For the non-inheritance relationship, the main class InitialOrderWithoutExtend contains an instance of the static member variable (class variable) SampleClass class, two instances of the ordinary member variable SampleClass class (in different order in the program) and a static code block, in which the static In the code block, if the static member variable sam is not empty, change the reference to sam. Two main class objects are created in the main() method, and the attribute s of the static member sam of the two main class objects is printed.
Code 1 :
package com.j2se; public class InitialOrderWithoutExtend { static SampleClass sam = new SampleClass("Static member sam initialization"); SampleClass sam1 = new SampleClass("Initialize common member sam1"); static { System.out.println("static block execution"); if (sam == null) System.out.println("sam is null"); sam = new SampleClass("Initialize the sam member variable in the static block"); } SampleClass sam2 = new SampleClass("Initialize common member sam2"); InitialOrderWithoutExtend() { System.out.println("InitialOrderWithoutExtend default constructor is called"); } public static void main(String[] args) { // Create the first main class object System.out.println("The first main class object: "); InitialOrderWithoutExtend ts = new InitialOrderWithoutExtend(); // Create the second main class object System.out.println("The second main class object: "); InitialOrderWithoutExtend ts2 = new InitialOrderWithoutExtend(); // View the static members of the two main class objects: System.out.println("Static objects of 2 main class objects: "); System.out.println("The first main class object, static member sam.s: " + ts.sam); System.out.println("The second main class object, static member sam.s: " + ts2.sam); } } class SampleClass { // SampleClass cannot contain any member variables of the main class InitialOrderWithoutExtend // Otherwise, it will cause circular reference, circular initialization, and call stack depth is too large // throw StackOverFlow exception // static InitialOrderWithoutExtend iniClass1 = new InitialOrderWithoutExtend("Initialization of static member iniClass1"); // InitialOrderWithoutExtend iniClass2 = new InitialOrderWithoutExtend("Initialization of common member member iniClass2"); String s; SampleClass(String s) { this.s = s; System.out.println(s); } SampleClass() { System.out.println("SampleClass default constructor is called"); } @Override public String toString() { return this.s; } }
Output result:
static member sam initialization static block execution Initialize the sam member variable in a static block The first main class object: Ordinary member sam1 initialization Ordinary member sam2 initialization InitialOrderWithoutExtend default constructor is called The second main class object: Ordinary member sam1 initialization Ordinary member sam2 initialization InitialOrderWithoutExtend default constructor is called 2 static objects of main class objects: The first main class object, static member sam.s: initialize the sam member variable in the static block The second main class object, static member sam.s: initialize the sam member variable in the static block
It can be seen from the output results that the execution sequence is:
- static static code blocks and static members
- ordinary member
- constructor execution
When there are multiple static members and static code blocks or multiple ordinary members, the initialization order is the same as the order in which the members are declared in the program.
Notice that in the static code block of the program, the reference to the static member sam is modified. Two main class objects are created in the main() method, but as can be seen from the output, the static members and static code blocks are only initialized once, and the static members sam.s of the two newly created main class objects are the same. It can be seen that the static members and static code blocks of the class are initialized first in the class loading, and only once. Multiple instances of this class share static members, and the reference to the static member points to the last reference given by the program.
Initialization Order in Inheritance Relationships
Three classes are used here to verify the initialization order in the inheritance relationship: Father parent class, Son child class and Sample class. The parent class and the subclass respectively contain non-static code area, static code area, static members, and ordinary members. The main class at runtime is the InitialOrderWithExtend class, a subclass object is created in the main() method, and the Father object is used to point to the reference of the Son class instance (the parent class object points to the subclass reference, polymorphism).
Code 2 :
package com.j2se; public class InitialOrderWithExtend { public static void main(String[] args) { Father ts = new Son(); } } class Father { { System.out.println("Parent class non-static block 1 is executed"); } static { System.out.println("Parent class static block 1 executes"); } static Sample staticSam1 = new Sample("Initialize static member staticSam1 of parent class"); Sample sam1 = new Sample("Initialize the common member sam1 of the parent class"); static Sample staticSam2 = new Sample("Initialize static member staticSam2 of parent class"); static { System.out.println("The parent class static block 2 is executed"); } Father() { System.out.println("The default constructor of the parent class is called"); } Sample sam2 = new Sample("Initialize the common member sam2 of the parent class"); { System.out.println("Parent class non-static block 2 is executed"); } } class Son extends Father { { System.out.println("Subclass non-static block 1 executes"); } static Sample staticSamSub1 = new Sample("Subclass static member staticSamSub1 initialization"); Son() { System.out.println("Subclass default constructor is called"); } Sample sam1 = new Sample("Subclass common member sam1 initialization"); static Sample staticSamSub2 = new Sample("Subclass static member staticSamSub2 initialization"); static { System.out.println("Subclass static block 1 executes"); } Sample sam2 = new Sample("Initialization of subclass common member sam2"); { System.out.println("Subclass non-static block 2 executes"); } static { System.out.println("Subclass static block 2 executes"); } } class Sample { Sample(String s) { System.out.println(s); } Sample() { System.out.println("Sample default constructor was called"); } }
operation result:
The parent class static block 1 executes Parent class static member staticSam1 initialization Parent class static member staticSam2 initialization The parent class static block 2 is executed Subclass static member staticSamSub1 initialization Subclass static member staticSamSub2 initialization subclass static block 1 execute Subclass static block 2 executes Parent class non-static block 1 executes Parent class common member sam1 initialization Parent class common member sam2 initialization Parent class non-static block 2 executes The parent class default constructor is called Subclass non-static block 1 execute Subclass ordinary member sam1 initialization Subclass ordinary member sam2 initialization Subclass non-static block 2 executes Subclass default constructor is called
It can be seen from the output that the order of execution is:
- Parent class static code area and parent class static members
- Subclass static code area and subclass static members
- Parent class non-static code area and ordinary members
- parent class constructor
- Subclass non-static code area and ordinary members
- subclass constructor
Consistent with the initialization order in the non-inheritance relationship, the static code area and the parent class static members, non-static code area and ordinary members are at the same level. When there are multiple such code blocks or members, the initialization order and They are declared in the same order in the program; in addition, the static code area and static members are only initialized once, but during the initialization process, the references to static members can be modified.
Initialization sequence diagram
non-inheritance relationship
inheritance relationship
JVM interpretation of class initialization order
The class initialization sequence is controlled by the JVM class loading mechanism. The class loading mechanism includes steps such as loading, verification, preparation, parsing, and initialization. Whether in inheritance or non-inheritance relationship, the initialization order of classes is mainly affected by JVM class loading timing, resolution and clinit() initialization rules.
loading time
Loading is the first stage of the class loading mechanism. Only in the case of 5 active references , the class loading will be triggered, while other passive references will not trigger the class loading. For details on class loading timing and active and passive references in 5, see [In-depth Understanding of JVM]: Class Loading Mechanism . Three of the forms of active citations are:
- When the program startup needs to trigger the main method, the virtual machine will first trigger the initialization of this class
- Use the new keyword to instantiate an object, read or set a static field of a class (except for static fields that are final modified and put into the constant pool during JIT), and call a static method of a class, which will trigger initialization
- When initializing a class, if its parent class is not initialized, you need to trigger the initialization of its parent class first
Before triggering the main() method in code 1, you need to trigger the initialization of the main class InitialOrderWithoutExtend. After the main class initialization is triggered, after initializing the static code area and static members, print "the first main class object:", and then encounter new InitialOrderWithoutExtend ts = new InitialOrderWithoutExtend();
, and then initialize other common variables.
Code 2 is an inheritance relationship. Before the subclass is initialized, the initialization of the parent class must be triggered first.
Bottom-up recursion of class resolution in inheritance relationship
In the parsing phase of the class loading mechanism, the symbolic references in the constant pool are replaced by direct references, which are mainly aimed at 7 types of symbolic references such as classes or interfaces, fields, class methods, method types, method handles and call site qualifiers. For the class resolution process, see [In-depth Understanding of JVM]: Class Loading Mechanism .
In field parsing, class method parsing, and method type parsing, the rules of bottom - up recursive search and parsing in inheritance relationships are followed . It is the initialization order from top to bottom, from parent class to child class.
Initialize the clinit() method
The initialization phase is the process of executing the class constructor method clinit(). clinit() is generated by the compiler automatically collecting the assignment actions of all class variables (static variables) in the class and combining the static statement blocks. The order in which the compiler collects is determined by the order in which the statements appear in the source file. The JVM will ensure that the clinit() method of the parent class has been executed before the clinit() method of the subclass is executed.
Therefore, in all initialization processes, the clinit() method ensures that static variables and static statement blocks are always initialized first, and the parent class clinit() must be executed first, and then the subclass clinit() is executed.
Code Order and Object Memory Layout
In the previous analysis, we saw that the initialization of a class has a relatively fixed order: static code areas and static variables precede non-static code areas and ordinary members, and precede constructors. In the initialization process of the same level, the initialization order is consistent with the order in which the variables are defined in the program.
The code order also affects the object memory layout. (For details on the memory layout of JVM objects, see [In-depth Understanding of JVM]: Java Object Creation, Memory Layout, and Access Positioning .)
In the HotSpot virtual machine, the layout of objects stored in memory can be divided into three areas: object header (Header), instance data (Instance Data) and alignment padding (Padding). The instance data is the effective information that the object actually stores, and it is also the field content of various types defined in the program code.
Whether it is inherited from the parent class or defined by the subclass, it needs to be recorded. The storage order of this part of the JVM parameters and fields affects the definition order of the program source code. The default allocation strategy of the HotSpot virtual machine is longs/doubles, ints, shorts/chars, bytes/booleans, and oop. It can be seen from the allocation strategy that fields of the same width are always allocated together. If this condition is met, the variables defined in the parent class will appear before the child class. However, if the JVM parameter CompactFields is enabled (default true, enabled), then narrower variables in subclasses may also be inserted into the gaps in parent class variables.
Inheritance and initialization:
public class Child extends parent { public static void main(String[] args){ } } class parent{ }
Loading process:
1. First visit Child.main(). So the loader starts and finds the compiled code for the Child class (in a file named Child.class), and in the process of loading it, the compile time notices that it has a base class Parent (represented by the keyword extends Learned), so the Parent class is loaded.
2. If the base class has its own base class, then the second base class will be loaded, and so on.
3. The static initialization in the root base class will be executed, followed by the next exported class, and so on.
So far, the necessary classes have been loaded. (loading order: current class --> base class static initialization order: base class --> current class)
Create object:
All primitive types in the object are set to default values, and object references are set to null.
The constructor of the base class will be called