[Transfer] [In-depth understanding of JVM]: Initialization order in Java class inheritance

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:

  1. static static code blocks and static members
  2. ordinary member
  3. 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:

  1. Parent class static code area and parent class static members
  2. Subclass static code area and subclass static members
  3. Parent class non-static code area and ordinary members
  4. parent class constructor
  5. Subclass non-static code area and ordinary members
  6. 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

Non-inheritance relationship initialization order

inheritance relationship

Inheritance relationship initialization order

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

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326678951&siteId=291194637