The order in which Java classes are initialized is often confusing, and now this article attempts to experiment with the initialization order of classes in Java non-inheritance and inheritance relationships from a JVM perspective, trying to give an explanation of the JVM's perspective.
initialization order in non-inheritance relationships
For non-inheritance relationships, the main class initialorderwithoutextend contains an instance of the static member variable (class variable) SampleClass class, and the normal member variable SampleClass The 2 instances of the class (in a different order in the program) and a static code block, where the static member variable Sam is not empty, changes the SAM reference. The main () method creates 2 main class objects, prints the static members of the 2 main class object, Sam's property S.
Code Listing 1:
Package com.j2se;PublicClassInitialorderwithoutextend {static SampleClass Sam =New SampleClass ("Static member Sam initialization"); SampleClass sam1 =New SampleClass ("Normal member sam1 initialization");static {System.out.println ("Static block Execution");if (Sam = =NULL) SYSTEM.OUT.PRINTLN ("Sam is null"); Sam =New SampleClass ("Initialize SAM member variables within a static block"); } SampleClass sam2 =New SampleClass ("Normal member Sam2 initialization"); Initialorderwithoutextend () {System.out.println ("Initialorderwithoutextend default constructor is called"); }PublicStaticvoidMain (string[] args) {Creates a 1th main class object System.out.println ("1th Main Class object:"); Initialorderwithoutextend ts =New Initialorderwithoutextend ();Creates a 2nd main class object System.out.println ("2nd Main Class object:"); Initialorderwithoutextend TS2 =New Initialorderwithoutextend ();View static members of two main class objects: System.out.println ("Static objects of 2 main class objects:"); System.out.println ("1th main class object, static member Sam.s:" + Ts.sam); System.out.println ( "2nd main class object, static member Sam.s:" + Ts2.sam);}} Class SampleClass {//SampleClass cannot contain any of the main class Initialorderwithoutextend member variables //Otherwise causes circular reference, loop initialization, call stack depth too large //throw StackOverflow exception //static initialorderwithoutextend IniClass1 = new Initialorderwithoutextend (" Static member IniClass1 initialization "); //initialorderwithoutextend iniClass2 = new Initialorderwithoutextend (" Ordinary member member INICLASS2 initialization "); 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:
静态成员sam初始化static块执行静态块内初始化sam成员变量第1个主类对象:普通成员sam1初始化普通成员sam2初始化InitialOrderWithoutExtend默认构造函数被调用第2个主类对象:普通成员sam1初始化普通成员sam2初始化InitialOrderWithoutExtend默认构造函数被调用2个主类对象的静态对象:第1个主类对象, 静态成员sam.s: 静态块内初始化sam成员变量第2个主类对象, 静态成员sam.s: 静态块内初始化sam成员变量
The output shows that the order of execution is:
- Static code blocks and static members
- Ordinary Members
- Constructor execution
When you have more than one static member and a static code block or multiple ordinary members, the order of initialization and the order in which members are declared in the program are consistent.
Notice that in the static code block of the program, a reference to the static member Sam is modified. The main () method creates 2 main class objects, but the output shows that static members and static code blocks are initialized only once, and the static member Sam.s of the new 2 main class object is the same. Therefore, static members and static code blocks of a class are initialized first and only once in class loading. Multiple instances of the class share a static member, and a reference to the static member points to the last reference given by the program.
the order of initialization in an inheritance relationship
There are 3 classes used here to verify the order of initialization in an inheritance relationship: Father parent, son, and sample classes. The parent and child classes each contain non-static code areas, static code areas, static members, and ordinary members. The main class of the runtime is the Initialorderwithextend class, which creates an object of a subclass in the main () method, and uses the Father object to point to a reference to the Son class instance (the parent class object points to the subclass reference, polymorphic).
Code Listing 2:
Package com.j2se;PublicClassInitialorderwithextend {PublicStaticvoidMain (string[] args) {Father ts =New Son (); }}class Father {{System.out.println ("Parent class non-static block 1 execution"); }static {System.out.println ("Parent class static block 1 execution"); }static Sample staticSam1 =New Sample ("Parent class static member staticSam1 initialization"); Sample sam1 =New Sample ("Normal member of parent class SAM1 initialization");static Sample staticSam2 =New Sample ("Parent class static member StaticSam2 initialization");static {System.out.println ("Parent class static block 2 Execution"); } Father () {System.out.println ("Parent class default constructor is called"); } Sample sam2 =New Sample ("Normal member of parent class SAM2 initialization"); {System.out.println ("Parent class non-static block 2 execution"); }}class Son extends Father {{System.out.println ("Subclass Non-static block 1 execution"); }static Sample StaticSamSub1 =New Sample ( "sub-class default constructor is called");} Sample sam1 = new sample ( "Subclass Ordinary members sam1 initialize"); static Sample staticSamSub2 = new sample (" subclass Static members STATICSAMSUB2 initialization "); static {System.out.println ( "subclass static block 1 Execution");} Sample sam2 = new sample ( "subclass Generic member Sam2 initialize"); {System.out.println ( "subclass non-static block 2 Execution");} static {System.out.println ( "subclass static block 2 Execution");}} Class Sample {sample (String s) {System.out.println (s);} Sample () {System.out.println ( "sample default constructor is called");}}
Operation Result:
static块 1 执行父类 静态成员 staticSam1 初始化父类 静态成员 staticSam2 初始化父类 static块 2 执行子类 静态成员 staticSamSub1 初始化子类 静态成员 staticSamSub2 初始化子类 static块1 执行子类 static块2 执行父类 非静态块 1 执行父类 普通成员 sam1 初始化父类 普通成员 sam2 初始化父类 非静态块 2 执行父类 默认构造函数被调用子类 非静态块 1 执行子类 普通成员 sam1 初始化子类 普通成员 sam2 初始化子类 非静态块 2 执行子类 默认构造函数被调用
The output shows that the order of execution is:
- Parent class static code area and parent class static member
- Subclass static code area and subclass static members
- Parent class non-static code area and normal member
- Parent class Constructors
- Subclass non-static code area and normal member
- Sub-class constructors
Consistent with the order of initialization in a non-inheritance relationship, the static code area and the parent class static members, non-static code areas, and ordinary members are of the same level, and when there are multiple such blocks or members, the order of initialization is consistent with the order in which they are declared in the program; Static code areas and static members are also initialized only once, but you can modify the references to static members during initialization.
Initialization sequence Diagram
Non-inheritance relationships
Inheritance relationship
JVM Interpretation of class initialization order
The class initialization order is controlled by the JVM class loading mechanism, including loading, validating, preparing, parsing, initializing, and so on. In both inheritance and non-inheritance relationships, the initialization order of classes is primarily affected by the JVM class loading timing, parsing, and Clinit () initialization rules.
Loading Time
loading is the first phase of the class loading mechanism, which triggers the loading of the class only in the case of 5 active references , and does not trigger the loading of the class in the case of other passive references. See "Understanding the JVM" For more information about the class loading time and the active and passive references in 5: class loading mechanism. 3 of these active references are in the form of:
- When the program starts to trigger the main method, the virtual opportunity first triggers the initialization of the class.
- Instantiating an object using the New keyword, reading or setting a static field of a class (except for a static field that is final decorated, JIT-placed into a constant pool), invoking a static method of a class, triggers the initialization
- When initializing a class, if its parent class is not initialized, it needs to trigger the initialization of its parent class first
Before the main () method is triggered in code 1, it is necessary to trigger the initialization of the main class Initialorderwithoutextend, after the main class initialization is triggered, after the static code area and static members are initialized, the "1th Main class object:" is printed, and then a new is encountered. InitialOrderWithoutExtend ts = new InitialOrderWithoutExtend();
The initialization of other ordinary variables.
Code 2 is an inheritance relationship, and the initialization of the parent class must be triggered before the subclass is initialized.
The bottom-up recursion of class parsing in inheritance relationship
The parsing phase of the class loading mechanism replaces the symbolic reference in a constant pool with a direct reference, primarily for a class or interface, a field, a class method, a method type, a method handle, and a call Point qualifier 7 class symbol reference. The parsing process for classes is described in "Understanding the JVM": Class loading mechanism.
But in the field parsing, the class method parsing, the method type resolution, all follow the inheritance relationship in the bottom-up recursive search parsing rules, because of the recursive characteristics (that is, the data structure of the stack "LIFO"), the initialization process is from the top down, from the parent class to the subclass of the initialization sequence.
Initialize the Clinit () method
The initialization phase is the process of executing the class constructor method Clinit (). Clinit () is the result of an assignment of all class variables (static variables) in the compiler's auto-collection class and a static statement block merge. The order that the compiler collects is determined by the order in which the statements appear in the source file. The JVM guarantees that the Clinit () method of the parent class has been executed before the subclass's Clinit () method executes.
Therefore all initialization process Clinit () method ensures that static variables and static statement blocks are always initialized first, and must first execute the parent class Clinit (), in the execution subclass of Clinit ().
code Order vs. Object Memory Layout
As we saw in the previous analysis, the initialization of classes has a relatively fixed order: static code areas and static variables precede non-static code areas and ordinary members, before constructors. During initialization at the same level, the initialization order is consistent with the variable definition in the program's order.
The code order also has an effect on the object memory layout. (For the JVM object memory layout See "Understanding the JVM": Creation of Java objects, memory layout, access positioning.) )
In a hotspot virtual machine, the layout of objects stored in memory can be divided into 3 areas: Object Header (header), instance data (Instance), and aligned padding (Padding). The instance data is the valid information that the object actually stores, as well as the various types of field content defined in the program code.
Whether it is inherited from a parent class or defined by a subclass, it is necessary to record that this part of the stored order JVM parameters and fields are defined in the program source code in the order of effect. The default allocation policy for the hotspot virtual machine is longs/doubles, INTs, Shorts/chars, Bytes/booleans, and OOP, as can be seen from the allocation policy, where fields of the same width are always assigned together. If this condition is met, the variables defined in the parent class will appear before the subclass. However, if the JVM parameter compactfields is enabled (enabled by default), then the narrower variable in the subclass may also be inserted into the void of the parent class variable.
Initialization order in Java class inheritance relationships