In many Java interviews, we often see a review of the Java class loading mechanism, such as the following question:
Classgrandpa{Static {System.out.println ("Grandpa in Static code block"); }}ClassFatherExtendsgrandpa{Static {System.out.println ("Papa in Static code block"); } public static int factor =25; PublicFather () { System.out.println ("I am Father ~");}} class son extends father{static { System.out.println ("Son in Static code block");} public son () { Sys Tem.out.println ("I am the Son ~");} Public class initializationdemo{public static void Main (string[] args) { System.out.println (" Father's age: "+ son.factor); //Entrance}}
Please write the final output string.
The correct answer is:
爷爷在静态代码块爸爸在静态代码块爸爸的岁数:25
I believe many students see this topic, the expression is collapsed, completely do not know where to start. Some even encountered several times, still can not find the right answer ideas.
In fact, this kind of face test is your understanding of the Java class loading mechanism.
If you do not understand the Java loading mechanism, then you are unable to solve the problem.
So this article, I first take you to learn the basics of Java class loading, and then the actual combat analysis of several topics to let everyone grasp the idea.
Let's start with the seven phases of the Java class loading mechanism.
Seven stages of the Java class loading mechanism
When our Java code is compiled, the corresponding class file is generated. Then when we run java Demo
the command, we actually start the JVM virtual machine to execute the contents of the class bytecode file. The process of executing a class bytecode by a JVM virtual machine can be divided into seven phases: load, validate, prepare, parse, initialize, use, unload.
Load
The following is the most official description of the loading process.
The load phase is the first phase of the class loading process. At this stage, the main purpose of the JVM is to convert the bytecode from each location (network, disk, etc.) into a binary byte stream to be loaded into memory, and then create a corresponding class object in the JVM's method area for this class object, which is the access entry for this class of data.
In fact, the loading phase is in a nutshell: Loading code data into memory. This process is not directly related to our solution to this problem, but it is a process of class loading mechanism, so we have to mention it.
Verify
After the JVM has loaded the class bytecode file and created the corresponding class object in the method area, the JVM initiates a checksum of that byte stream, and only files conforming to the JVM bytecode specification can be executed correctly by the JVM. This verification process can be broadly divided into the following types:
- JVM canonical checksum. The JVM performs a file format check on the byte stream to determine if it is compliant with the JVM specification and whether it can be processed by the current version of the virtual machine. For example: whether the file is at the
0x cafe bene
beginning and whether the primary and secondary version number is within the current virtual machine processing range.
- code logic check. The JVM verifies the data flow and control flow of the code, ensuring that the JVM does not have a fatal error after running the bytecode file. For example, a method requires a parameter of type int, but when it is used, it passes in a parameter of type String. A method requires the result of a String type to be returned, but the result is not returned at the end. The code references a class named Apple, but you don't actually define the Apple class.
When the code data is loaded into memory, the virtual machine verifies the code data to see if the code is actually written according to the JVM specification. This process is not directly related to our solution, but understanding the class loading mechanism must be known to have this process.
Preparation (emphasis)
After the checksum of the bytecode file is completed, the JVM begins allocating memory and initializing the class variable. There are two key points to note here, the objects that are allocated to memory, and the types of initialization.
- the memory allocation object. variables in Java have "class variables" and "Class member variables", "class variables" refers to variables that are modified by static, and all other types of variables belong to "class member variables". In the preparation phase, the JVM allocates memory only for class variables, not for class member variables. The memory allocation for class member variables needs to wait until the initialization phase to begin.
For example, the following code will only allocate memory for the factor property in the preparation phase, not memory for the website property.
public static int factor = 3;public String website = "www.cnblogs.com/chanshuyi";
- the type of initialization. during the preparation phase, the JVM allocates memory for the class variable and initializes it. But the initialization here refers to assigning the variable a value of 0 to the data type in the Java language, not the value initialized in the user code.
For example, after the preparation phase of the following code, the value of sector will be 0 instead of 3.
public static int sector = 3;
But if a variable is a constant (static final decoration), then in the preparation phase, the property is given the value that the user wants. For example, after the preparation phase of the following code, the value of number will be 3 instead of 0.
public static final int number = 3;
Static final is copied directly, and the static variable is given a value of 0. In fact, we can think it over a little bit to understand.
The difference between the two statements is that one has the final keyword modifier, and the other one does not. The final keyword in Java represents an immutable meaning, meaning that the value of number will not change once it is assigned. Since once the assignment is no longer changed, it must be given to the user at the outset, so the final modified class variable is given the desired value in the preparation phase. Without a class variable that is final modified, it may change during the initialization phase or at run time, so it is not necessary to give the user the desired value in the preparation phase.
Analytical
After the prep phase, the JVM resolves for classes or interfaces, fields, class methods, interface methods, method types, method handles, and call Point qualifier 7 class references. The primary task of this phase is to replace the symbolic reference in the constant pool with a direct reference to it directly in memory.
In fact, this stage for us is almost transparent, understand it.
Initialize (focus)
In the initialization phase, the user-defined Java program code will actually start executing. At this stage, the JVM initializes class objects based on the order in which they are executed, typically triggering initialization when the JVM encounters the following 5 scenarios:
- When you encounter the four bytecode directives of new, getstatic, putstatic, Invokestatic, if the class has not been initialized, you need to trigger its initialization first. The most common Java code scenario for generating these 4 instructions is when instantiating an object using the New keyword, reading or setting a static field of a class (except for the static field that was final decorated, which has been placed in a constant pool by the compiler), and when invoking a static method of a class.
- When you use the Java.lang.reflect package method to make a reflection call to a class, if the class has not been initialized, you need to trigger its initialization first.
- When initializing a class, it is necessary to trigger the initialization of its parent class if it finds that its parent class has not yet been initialized.
- When the virtual machine starts, the user needs to specify a main class to execute (the class that contains the main () method), and the virtual opportunity initializes the main class first.
- When using JDK1.7 Dynamic language support, if a java.lang.invoke.MethodHandle instance finally resolves the result ref_getstatic,ref_putstatic,ref_invokestatic the method handle, And the class that corresponds to this method handle is not initialized, it needs to be initialized before it is triggered.
See the above several conditions you may faint, but it does not matter, do not need to back, know a bit better, back to use when back to look for a bit.
Use
When the JVM completes the initialization phase, the JVM begins executing the user's program code from the Ingress method. This stage is just a bit of an understanding.
Unloading
When the user program code finishes executing, the JVM starts destroying the created Class object, and the JVM that runs the last run also exits memory. This stage is just a bit of an understanding.
After reading the Java class loaded wit, is not a little bit ignorant of it. Not afraid, we first wake up God by a small example.
PublicClassBook {Publicstatic void main (string[] args) {system. Out.println ( "Hello Shuyi."); Book () {system. Out.println (out.println ( "price=" + Price +out.println ( "ordinary code block of the book");} int price = 110; static {system. Out.println (static int amount = 112;}
Think about what the above code output is?
Give you 5 minutes to think, 5 minutes after the handing over, haha.
Well, think about it and announce the answer.
书的静态代码块Hello ShuYi.
What, are you correct? Is it a little different from what you think?
Let's take a brief look at the 4th of the 5 scenarios in which the initialization is triggered (when the virtual machine starts, the user needs to specify a main class to execute (the one that contains the main () method), the virtual opportunity initializes the main class first, and we initialize the class.
So what is the initialization order of classes?
Here comes the point!
Here comes the point!
Here comes the point!
In our code, we only know that there is a construction method, but in fact, after the Java code is compiled into bytecode, there is no concept of constructing methods, only the class initialization method and the object initialization method.
So how did these two approaches come about?
- Class initialization method. The compiler collects assignment statements of class variables, static blocks of code in the order in which they appear, and ultimately the class initialization method. class initialization methods are typically executed when the class is initialized.
In the above example, the class initialization method is the following code:
static { System.out.println("书的静态代码块"); } static int amount = 112;
- The object initialization method. The compiler collects the assignment statements of member variables, ordinary blocks of code, and finally the code that collects the constructors, in the order in which they appear, and ultimately the object initialization method. object initialization methods are typically performed when instantiating a class object.
In this example, the object initialization method is the following code:
{ System.out.println("书的普通代码块"); } int price = 110; System.out.println("书的构造方法"); System.out.println("price=" + price +",amount=" + amount);
After the class initialization method and the object initialization method, we look at this example, we can not difficult to draw the above answer.
But careful friends will find that, in fact, the above example does not actually implement the object initialization method.
Because we did not instantiate the book class object. If you add the new book () statement to the main method, you will find that the initialization method of the object is executed!
Interested friends can try it on their own, I will not carry out here.
Through the above theory and simple examples, we enter the more complex actual combat analysis it!
Real-Combat analysis
Classgrandpa{Static {System.out.println ("Grandpa in Static code block"); }}ClassFatherExtendsgrandpa{Static {System.out.println ("Papa in Static code block"); } public static int factor =25; PublicFather () { System.out.println ("I am Father ~");}} class son extends father{static { System.out.println ("Son in Static code block");} public son () { Sys Tem.out.println ("I am the Son ~");} Public class initializationdemo{public static void Main (string[] args) { System.out.println (" Father's age: "+ son.factor); //Entrance}}
Think about what the final output of the above code is?
The resulting output is:
爷爷在静态代码块爸爸在静态代码块爸爸的岁数:25
Maybe someone will ask why not output "son in static code block" This string?
this is because for static fields, only classes that directly define the field are initialized (execution of static code blocks). therefore, by referencing a static field defined in the parent class through its subclasses, only the initialization of the parent class is triggered, not the initialization of the subclass.
On the opposite side of this example, we can start analyzing the analysis from the entrance:
- First the program to the main method here, using normalization to output the factor class member variable in the Son class, but the son class does not define this class member variable. So we looked for the parent class, we found the corresponding class member variable in the Father class, and then triggered the initialization of the Father.
- But according to the 3rd of the 5 scenarios we said above (when initializing a class, it is necessary to trigger the initialization of its parent class if it finds that its parent class has not yet been initialized). We need to initialize the parent class of the Father class first, that is, initialize the Grandpa class and then initialize the Father class. So we first initialize the Grandpa class output: "Grandpa in Static code block", and then initialize the Father class output: "Dad in static code block."
- Finally, after all the parent classes are initialized, the Son class can invoke the static variables of the parent class to output: "Daddy's Age: 25". "
How, do not feel enlightened.
Let's look at a more complicated example to see what the output is.
Classgrandpa{Static {System.out.println ("Grandpa in Static code block"); } publicGrandpa () {System.out.println ("I Am Grandpa ~"); }}ClassFatherExtendsgrandpa{Static {system.out.println ( "Papa in Static code block"); father () {system.out.println ( "I am a Father ~") ; }}class son extends father{static { System.out.println ( "son in static code block"), public son () {system.out.println (" I am a son ~ ");} Public class initializationdemo{ public static void Main (string[] args) {new son (); //Portal}}
The output is:
爷爷在静态代码块爸爸在静态代码块儿子在静态代码块我是爷爷~我是爸爸~我是儿子~
How, do not think this problem and above is different.
Let's take a closer look at the execution process of the above code:
- First, at the entrance, we instantiate a son object, so it triggers the initialization of the Son class, and the initialization of the son class drives the initialization of the Father, Grandpa class to execute the static block of code in the corresponding class. will therefore output: "Grandpa in Static code block", "Papa in Static code block", "Son in static code block".
- When the son class completes the initialization, it calls the son class's construction method, and the son class constructs the method the call also will drive the Father, the Grandpa class constructs the method the call, finally will output: "I am the grandfather ~", "I am the Father ~", "I am the son ~". "
After reading the two examples, I believe we have a full chest.
Here is a special point for everyone to see an example, a bit difficult oh!
PublicClassBook {PublicStaticvoidMainString[] args) {staticfunction ();}Static Book book =new book ();static {System.Out.println ("Static code block of the book"); } {System. out.println ("Ordinary code block of the book");} Book () {System. Out.println ("The construction method of the book"); System. out.println ("price=" + price +", amount=" + amount);} public static void staticfunction () {System. Out.println ("static method of the book");} int price = 110; static int amount = ;}
The output from the above example is:
书的普通代码块书的构造方法price=110,amount=0书的静态代码块书的静态方法
Let's step through the process of executing the code.
In the above two examples, because the main method is in the same class as there is no extra code, we have directly ignored the initialization of the class where the main method resides.
But in this case, the main method has a lot of code, and we can't just ignore it.
- When the JVM is in the preparation phase, it allocates memory and initializes the class variables. At this point, our book instance variable is initialized to the Null,amount variable initialized to 0.
- When the initialization phase is entered, because the book method is the entry of the program, according to the class five case initialization we mentioned above (when the virtual machine starts, the user needs to specify a main class to execute (the class that contains the main () method), the virtual opportunity initializes the main class first. So the JVM initializes the book class, which is the execution of the class constructor.
- The JVM initializes the book class by executing the class constructor (which consists of the class constructor in order to collect all static code blocks and class variable assignment statements in the Class), and then executes the constructor of the object (in order to collect the member variable assignments and ordinary blocks of code, and finally to gather the object constructors, eventually composing the object constructor).
For the book class, its class construction method () can be simply represented as follows:
static Book book = new Book();static{ System.out.println("书的静态代码块");}static int amount = 112;
The first static Book book = new Book();
statement is executed, which in turn triggers the instantiation of the class. The JVM then executes the object constructor and collects the object constructor code:
{ System.out.println("书的普通代码块");}int price = 110;Book(){ System.out.println("书的构造方法"); System.out.println("price=" + price +", amount=" + amount);}
At this point, the price is given a value of 110, output: "The ordinary code block of the book", "the construction method of the book." At this point, the value of price is 110, and amount's assignment statement is not executed, so only the 0 value given in the preparation phase, so the output "price=110,amount=0".
After the class instantiation is complete, the JVM continues to initialize the class constructor:
static Book book = new Book(); //完成类实例化static{ System.out.println("书的静态代码块");}static int amount = 112;
That is, the output: "Static code block of the book", after which a value of 112 is given to amount.
- Here, the initialization of the class is complete and the JVM executes the contents of the main method.
public static void main(String[] args){ staticFunction();}
i.e. output: "Static method of the book".
Methodology
As can be seen from the above examples, it is possible to analyze the execution order of a class by following these steps:
- determines the initial value of the class variable. during the preparation phase of class loading, the JVM initializes a 0 value for the class variable, at which time the class variable has an initial value of 0. If the class variable is final modified, it is directly initialized to the user's desired value.
- initializes the Ingress method. when the initialization phase of the class load is entered, the JVM looks for the entire main method entry to initialize the entire class where the Main method resides. When a class needs to be initialized, the class constructor () is initialized first, and then the object constructor () is initialized.
- initializes the class constructor. the JVM collects assignment statements of class variables, static blocks of code in order, and eventually the class constructor is executed by the JVM.
- initializes the object constructor. the JVM will follow the assignment statements of the collection member variables, ordinary blocks of code, and finally collect the construction methods, which are composed of the object constructors, which are ultimately executed by the JVM.
If the initialization of another class is encountered when initializing the class of the main method, the corresponding class is loaded and returned after the load is complete. This loops back and forth, eventually returning the class where the main method resides.
Transferred from: https://www.cnblogs.com/chanshuyi/p/the_java_class_load_mechamism.html#%E5%AE%9E%E6%88%98%E5%88%86%E6%9E%90
Java class loading mechanism