Home  >  Article  >  Java  >  Let you understand the static keyword in Java at once

Let you understand the static keyword in Java at once

醉折花枝作酒筹
醉折花枝作酒筹forward
2021-08-04 17:49:462148browse

I believe that many students have encountered this kind of question. They may have checked the information and then forgotten it. If they encounter it again, they still cannot answer it correctly. Next, through 4 steps, I will take you to disassemble the execution sequence of this code and summarize the rules.

The opening question is to examine the order of code execution:

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

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

    public Parent() {
        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() {
        System.out.println("Child constructor block");
    }
}

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

    public Hobby() {
        System.out.println("hobby constructor block");
    }
}

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

I believe that many students have encountered this kind of problem. They may have checked the information and then forgotten it. If they encounter it again, they still cannot answer it correctly. Next, the class representative will take you through 4 steps to dismantle the execution sequence of this code and summarize the rules.

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() {
        System.out.println("Child constructor block");
    }
}

Child.class after compilation

public class Child extends Parent {
    private Hobby hobby;

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

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

Passed From the comparison, we can see that the compiler moves the assignment operations of the initialization block and instance fields to before the constructor code, and retains the order of related codes. In fact, if there are multiple constructors, the initialization code will be copied and moved over.

Based on this, the first priority order can be drawn:

  • Initialization code > Constructor code

2. What does static do?

The loading process of a class can be roughly divided into three stages: Loading-> Link-> Initialization

The initialization stage can be triggered by 8 situations Zhou Zhiming》P359 "8 types of triggering class initialization Situation ") trigger:

  • When using the new keyword to instantiate an object

  • Read or set a static field of a type (constant" )

  • Calling a static method of a type

  • When using reflection to call a class

  • When initializing a class, if it is found that the parent class has not been initialized, the parent class initialization will be triggered first.

  • When the virtual machine starts, the main class (including main() will be initialized first. ) method)

  • When the MethodHandle instance is called for the first time, initialize the class where the method pointed to by the MethodHandle is located.

  • If in the interface A default method (default modified interface method) is defined, and the implementation class of the interface is initialized, then the interface must be initialized before it

Items 2 and 3 are Triggered by static code.

In fact, the initialization phase is the process of executing the class constructor583d030be372af71281df966e84181a5 method. This method is automatically generated by the compiler, and it collects the assignment actions and static modifications of all class variables modified by static. statement block (static{} block), and retain the order in which these codes appear.

According to item 5, the JVM will ensure that before the subclass's 583d030be372af71281df966e84181a5 method is executed, the parent class's 583d030be372af71281df966e84181a5 The method has been executed.

To summarize: accessing class variables or static methods will trigger the initialization of the class, and the initialization of the class is to execute 583d030be372af71281df966e84181a5, that is, to execute the static modified assignment action and static{} block, and the JVM guarantees that the parent class initialization is performed first, and then the subclass initialization is performed.

This leads to the second priority order:

  • static code of the parent class> Static code of subclasses

3.static code is only executed once

We all know that static code (except static methods) is only executed once.

You have Never thought about how this mechanism is guaranteed?

The answer is: Parental delegation model.

The parental delegation model of JDK8 and before is:

Application class loading Device → Extension Class Loader → Start Class Loader

Classes written in normal development are loaded by the application class loader by default, and it will be delegated to its parent class: the extension class loader. The extension class loader in turn delegates to its parent class: the startup class loader. Only when the parent class loader reports that it cannot complete the loading request, the child loader will try to complete the loading by itself. This process is parent delegation. The parent-child relationship between the three is not achieved through inheritance, but through the combination mode.

The implementation of this process is also very simple. The key implementation code is shown below:

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
    // 首先检查该类是否被加载过
    // 如果加载过,直接返回该类
    Class<?> c = findLoadedClass(name);
    if (c == null) {
        try {
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {
            // 如果父类抛出ClassNotFoundException
            // 说明父类无法完成加载请求
        }

        if (c == null) {
            // 如果父类无法加载,转由子类加载
            c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
    return c;
}

With the comments, I believe it is easy for everyone to understand.

It can be seen from the code delegated by parents that under the same class loader, a class can only be loaded once, which limits it to only be initialized once. Therefore, the static code in the class (except static methods) is only executed once when the class is initialized

4. 7e51f00a783d7eb8f68358439dee7daf and 583d030be372af71281df966e84181a5

As mentioned earlier, the compiler automatically generates Class constructor: 583d030be372af71281df966e84181a5 method, it will collect the assignment actions and static statement blocks (static{} blocks) of all static-modified class variables and retain the order of appearance of the code. It will be executed when the class is initialized

Correspondingly, the compiler will also generate an 7e51f00a783d7eb8f68358439dee7daf method, which will collect the assignment actions of the instance fields, the code in the initialization statement block ({} block) and the constructor (Constructor), and retain the order of appearance of the code. , it will be executed after the new instruction

So, when we new a class, if the JVM has not loaded the class, it will be initialized first and then instantiated.

At this point, the third priority rule is ready to come out:

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

5. Regular practice

Combine the three previous rules and summarize the following two:

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

2. Static code of the parent class> Static code of the subclass

According to the previous summary, the initialization code and constructor code are collected by the compiler7e51f00a783d7eb8f68358439dee7daf , the static code is collected into 583d030be372af71281df966e84181a5, so the above rules are merged again:

Parent class583d030be372af71281df966e84181a5 > Subclass583d030be372af71281df966e84181a5 > Parent class 7e51f00a783d7eb8f68358439dee7daf > Subclass 7e51f00a783d7eb8f68358439dee7daf

Corresponds to the question at the beginning, let’s practice it:

When executing new Child(), the new keyword triggers the initialization of the Child class. When the JVM finds that it has a parent class, it first initializes the Parent class, starts executing the 583d030be372af71281df966e84181a5 method of the Parent class, and then executes the Child class. The 583d030be372af71281df966e84181a5 method (remember what is collected in 583d030be372af71281df966e84181a5?).

Then start instantiating an object of the Child class. At this time, we are ready to execute the 7e51f00a783d7eb8f68358439dee7daf method of Child. We find that it has a parent class. We will execute the 7e51f00a783d7eb8f68358439dee7daf method of the parent class first, and then execute the child class. of 7e51f00a783d7eb8f68358439dee7daf (remember what is collected in 7e51f00a783d7eb8f68358439dee7daf?).

I believe that after reading this, you already have the answer to the opening question. You might as well hand-write the output sequence first, and then write the code to verify it yourself.

Conclusion

static is often used in daily development. Every time I write, there are always two questions in my mind. Why should I use static? Is it okay without it?

As can be seen from this article, the application of static is far more than just class variables and static methods. In the classic singleton pattern, you will see various uses of static. The next article will write about how to write the singleton pattern in a fancy way.

The above is the detailed content of Let you understand the static keyword in Java at once. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete