preface

The entire loading process of Java classes was described in detail. Although, the length is long, but do not be intimidated by the content, in fact, each stage can be summed up in one sentence.

1) Load: Find and load the binary byte stream data of the class.

2) Validation: To ensure the correctness of the loaded classes.

3) Preparation: Allocate memory for static variables of the class and set default initial values.

4) Parsing: Convert symbolic references in a class to direct references.

5) Initialization: Assign correct initial values to static variables of the class.

Of course, to master the class loading mechanism, you need to study it in depth. (Ok, that’s a good one.) Because many of them are also common interview questions. For example, when I went to an interview, the interviewer asked a question about class initialization. That is, you give a piece of code, have a parent-child relationship, which includes static code blocks, construction code blocks, normal code blocks, constructors, and so on, and then you ask the code to determine the final order of execution. (You can think about it by yourself, the details of the specific content will not be extended for the time being)

Class loader

Finally, we come to the subject of this article — class loaders and parental delegation mechanisms.

In Understanding the Java Virtual Machine, class loaders are defined as follows:

The virtual machine design team implemented the class-loading action of obtaining the binary stream describing a class by its permission name outside the Java virtual machine so that the application could decide how to obtain the required classes. The code module that implements this action is called a class loader.

Simply put, the purpose of the classloader is to load the binary stream of the class.

There are three types of classloaders:

1) Start the Bootstrap ClassLoader, or root loader. The main purpose of this classloader is to load core apis such as rt.jar in the Java_Home/jre/lib directory that you configure locally

2) Extension ClassLoader. This loader is responsible for loading all jar packages in the Java_Home/jre/lib/ext directory.

3) Application ClassLoader. This loader loads the class libraries in your project’s ClassPath directory. If the user does not customize his own class loader, this is the program’s default class loader.

Alternatively, users can customize their own classloaders (to inherit the ClassLoader class) if necessary.

We can also print the classloader in code:

public class TestClassLoader { public static void main(String[] args) { Object obj = new Object(); System.out.println(obj.getClass().getClassLoader()); TestClassLoader t = new TestClassLoader(); System.out.println(t.getClass().getClassLoader()); System.out.println(t.getClass().getClassLoader().getParent()); System.out.println(t.getClass().getClassLoader().getParent().getParent()); }}Copy the code

Print result:

null
sun.misc.Launcher$AppClassLoader@58644d46
sun.misc.Launcher$ExtClassLoader@6d6f6e28
nullCopy the code

Note that the null in the first and fourth lines above does not mean empty; it means that the class loader is started. Null is returned because the startup class loader is implemented in C++ code and is not strictly a Java class, so Java code cannot access it. The second line is the application class loader, and the third line is the extension class loader.

Parent delegation mechanism

Before introducing the parent delegate mechanism, let’s see if the following code works correctly:

// define a java.lang package; public class String { public static void main(String[] args) { String s = new String(); System.out.println(s); }}Copy the code

The above code compiled without any problems, but when run, error:

The java.lang.String class does not contain the main method. The crux of the problem is that class loading follows the parent delegate mechanism.

Class loaders have the following hierarchy:

When a class is loaded, it delegates to its parent loader, and so on, until the top class loader is started. If the top layer cannot be loaded (that is, the corresponding class cannot be found), it will search the bottom layer until it is found. This is the parent delegate mechanism of a class.

What’s the good of that? This is equivalent to maintaining a hierarchy of priorities, always starting with the topmost parent loader. This is just like that when you encounter problems in your work, you need to give feedback, for example, first to the group leader, then the group leader to the superior manager, and finally the manager to the boss. Then the boss felt that the problem was too simple for him to handle himself, let the manager solve it himself, and then the manager handed it down to the team leader. The group leader a look, this problem is not difficult, people are also more enthusiastic, so help you solve the problem. (Maybe the example is not too appropriate, but the meaning can be understood)

At this point, we can see why the above code reported an error. Because of the parent delegate mechanism, when we load our own “java.lang.String” class, we end up delegating to the top level of the launcher and finding “java.lang.String” in the rt.jar package. Once found, we load rt.jar’s String class (which is the String class we use most often). We don’t need to look it up again, so we can’t load our own String class. Rt. jar does not have a main method in the String class.

Let’s just imagine if my code would have executed successfully without the parent delegate mechanism. If so, it means that I can overwrite classes (String, Integer, etc.) in rt.jar. This would throw the program into chaos, and the safety of the classes in the Java core package could not be guaranteed.