The foundation is weak and the earth shakes. Hi everyone, I am the class representative.

To learn more about Java, pay attention to the public account :Java class representative.

The opening question examines the order in which code is executed:

public class Parent {
    static {
        System.out.println("Parent static initial block");
    }

    {
        System.out.println("Parent initial block");
    }

    public Parent(a) {
        System.out.println("Parent constructor block"); }}public class Child extends Parent {
    static {
        System.out.println("Child static initial block");
    }

    {
        System.out.println("Child initial block");
    }
    
    private Hobby hobby = new Hobby();

    public Child(a) {
        System.out.println("Child constructor block"); }}public class Hobby {
    static{
        System.out.println("Hobby static initial block");
    }

    public Hobby(a) {
        System.out.println("hobby constructor block"); }}Copy the code

What does the above code output when new Child() is executed?

I believe that many students have encountered this kind of problem, may check the information and then forget, again encountered or wrong answer. Next, the class representative will take you through four steps to break down the execution sequence of this code and summarize the rules accordingly.

1. What does the compiler optimize?

The following two pieces of code compare the changes before and after compilation:

Child.java before compilation

public class Child extends Parent {
    static {
        System.out.println("Child static initial block");
    }
    {
        System.out.println("Child initial block");
    }
    
    private Hobby hobby = new Hobby();
    
    public Child(a) {
        System.out.println("Child constructor block"); }}Copy the code

Compiled child.class

public class Child extends Parent {
    private Hobby hobby;

    public Child(a) {
        System.out.println("Child initial block");
        this.hobby = new Hobby();
        System.out.println("Child constructor block");
    }

    static {
        System.out.println("Child static initial block"); }}Copy the code

As you can see by comparison, the compiler moves assignment of initialization blocks and instance fields ahead of constructor code and preserves the sequence of related code. In fact, if there are more than one constructor, the initialization code will be copied and moved around.

This leads to the first priority order:

  • Initialization code > constructor code

2. What does static do?

The loading process of a class can be roughly divided into three stages: load -> link -> initialization

The initialization phase can be triggered by eight conditions:

  1. When an object is instantiated using the new keyword
  2. Reads or sets static fields of a type (except constants)
  3. Calls a static method of a type
  4. When a class is called using reflection
  5. When initializing a class, if the parent class has not been initialized, the initialization of its parent class is triggered first
  6. When the VM starts, it initializes the main class (includingmain()Method class)
  7. The first time you call a MethodHandle instance, you initialize the class to which the method points.
  8. If a default method is defined in an interface (a default-modified interface method) and the implementation class of the interface is initialized, the interface is initialized before it

Items 2,3 are triggered by static code.

In fact, the initialization phase is the execution of the class constructor < Clinit > method, which is automatically generated by the compiler to collect the assignment actions and static statement blocks (static{} blocks) of all static modified class variables, preserving the order in which the code appears.

According to item 5, the JVM guarantees that the

methods of the parent class are executed before the

methods of the subclass are executed.

To recap: Accessing a class variable or static method triggers an initialization of the class, which is the execution of < Clinit >, the assignment of static modifiers and the static{} block, and the JVM guarantees that parent class initialization takes place before subclass initialization.

This leads to the second order of priority:

  • Of the parent classstaticCode > subclassstaticcode

3. Static code is executed only once

As we all know, static code (with the exception of static methods) executes only once.

Have you ever wondered how this mechanism is guaranteed?

The answer is: the parental delegation model.

The parent delegation model for JDK8 and before is:

Application class loader → Extension class loader → Start class loader

Classes written during normal development are loaded by default by the application class loader, which delegates to its parent: the extension class loader. The extension class loader in turn delegates to its parent: the start class loader. Only when the parent class loader reports that it is unable to complete the load request will the child loader attempt to complete the load itself, a process known as parental delegation. The father-son relationship of the three is not realized by inheritance, but by combination mode.

The implementation of this process is also simple, and the key implementation code is shown below:

protectedClass<? > loadClass(String name,boolean resolve) throws ClassNotFoundException
{
    // First check if the class has been loaded
    // If yes, return the class directlyClass<? > c = findLoadedClass(name);if (c == null) {
        try {
            if(parent ! =null) {
                c = parent.loadClass(name, false);
            } else{ c = findBootstrapClassOrNull(name); }}catch (ClassNotFoundException e) {
            // If the parent throws a ClassNotFoundException
            // Indicates that the parent class cannot complete the load request
        }

        if (c == null) {
            // If the parent class cannot be loaded, the subclass loadsc = findClass(name); }}if (resolve) {
        resolveClass(c);
    }
    return c;
}
Copy the code

I believe it is easy to understand with notes.

As the parent delegate code shows, a class can only be loaded once under the same classloader, which means it can only be initialized once. So static code in a class (except for static methods) is executed only once when the class is initialized

4. <init>and<clinit>

We’ve already described the compiler’s auto-generated class constructor: the

method, which collects the assignment actions and static statement blocks (static{} blocks) of all static modified class variables and preserves the code order, which is executed when the class is initialized

Accordingly, the compiler generates a

method that collects the assignment of the instance field, initializes the statement block ({} block), and the code in the Constructor, preserving the order in which the code appears, following the new instruction

So, when we new a class, if the JVM has not loaded the class, we initialize it first and then instantiate it.

This brings us to the third priority rule:

  • Static code (static{} block, static field assignment statement) > Initialization code ({} block, instance field assignment statement)

5. Practice regularly

The above three rules are combined to summarize the following two rules:

1. Static code (static{} block, static field assignment statement) > initialization code ({} block, instance field assignment statement) > Constructor code

2. Static code for the parent class > static code for the subclass

The initialization code and constructor code are collected by the compiler in

, and the static code is collected in

, so the above rules are merged again:

The parent class<clinit>> subclass<clinit>> the parent class<init>> subclass<init>

In response to the opening question, let’s practice:

When new Child() is executed, the new keyword triggers the initialization of the Child class. The JVM detects that it has a Parent class and initializes the Parent class to start executing the

method of the Parent class. Then execute the

method of the Child class (remember what was collected in

?). .


And then began to instantiate A Child class of objects, ready to perform at this time the Child of the < init > method, found that it has a parent class, priority of the parent class < init > method, and then execute the subclass < init > (remember < init > inside collect what?) .

I believe that seeing this, you have the answer to the opening question in mind, might as well write the output sequence by hand, and then write code to verify yourself.

conclusion

When I write static, I always have two questions in my mind. Why do I use static? Can’t I? This is exactly what the opening sentence says:

The foundation is weak and the earth shakes

As you can see from this article, the application of static is much more than class variables and static methods. In the classic singleton pattern, you’ll see various uses of static, and the next article will look at how to write a singleton pattern fancy.


【 Previous recommendation 】

Dubbo exception handling source code exploration and best practices

Use Spring Validation to elegantly validate parameters

Freemarker Tutorial (I)- Template development Manual

The RabbitMQ tutorial