To gain a deeper understanding of ClassLoader, you must first know what ClassLoader is used for. As the name implies, it is used to load Class files into the JVM for use by the program. We know that Java programs can dynamically load class definitions, and this dynamic loading mechanism is implemented through ClassLoader, so you can imagine how important ClassLoader is.
After seeing this, some friends may think of a question, that is, since ClassLoader is used to load classes into the JVM, how is ClassLoader loaded? Is it not a Java class?
That's right, there is indeed a ClassLoader here that is not written in Java language, but is part of the JVM implementation. This ClassLoader is bootstrap classloader (start class loader), this ClassLoader loads the Java core API when the JVM is running to meet the most basic needs of the Java program, including the user-defined ClassLoader. The so-called user-defined classLoader here refers to the ClassLoader implemented through the Java program. One is the ExtClassLoader. This ClassLoader is used to load the Java extension API, that is, the class in /lib/ext, and the other is the AppClassLoader. This ClassLoader is used to load the Class in the CLASSPATH setting directory on the user's machine. Usually, without specifying the ClassLoader, the programmer's customized classes are loaded by the ClassLoader.
When running a program, the JVM starts and runs bootstrap classloader. The ClassLoader loads the java core API (ExtClassLoader and AppClassLoader are also loaded at this time), then calls ExtClassLoader to load the extension API, and finally AppClassLoader loads the Class defined in the CLASSPATH directory. This is the most basic loading process of a program.
The above briefly explains the role of ClassLoader and the most basic loading process. Next, we will explain the way ClassLoader loads. Here we have to talk about the use of the parent delegate mode for class loading.
Each custom ClassLoader must inherit the abstract class ClassLoader, and each ClassLoader will have a parent ClassLoader. We can see that there is a getParent() method in the abstract class ClassLoader, which is used to return the parent of the current ClassLoader. Note that this parent does not refer to the inherited class, but a ClassLoader specified when instantiating the ClassLoader. If this parent is null, then the default parent of the ClassLoader is the bootstrap classloader. What is the use of this parent?
We can consider this situation. Suppose we have customized a ClientDefClassLoader and we use this custom ClassLoader to load java.lang.String, then will String be loaded by this ClassLoader? In fact, the java.lang.String class is not loaded by the ClientDefClassLoader, but is loaded by the bootstrap classloader. Why is this happening? In fact, this is the reason for the parent delegate mode, because before any custom ClassLoader loads a class, it will first delegate its father ClassLoader to load. It will only be loaded by itself after the father ClassLoader cannot be loaded successfully. In the above example, because java.lang.String is a class belonging to the java core API, so when using ClientDefClassLoader to load it, the ClassLoader will first delegate its father ClassLoader to load. As mentioned above, when the parent of ClassLoader is null, the parent of ClassLoader is the bootstrap classloader, so at the top level of ClassLoader is the bootstrap classloader, so finally delegate to bootstrap When the classloader is present, the bootstrap classloader will return the String Class.
Let's take a look at a piece of source code in ClassLoader:
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { // First check whether the class specified by the name is loaded Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { //If parent is not null, call parent's loadClass to load = parent.loadClass(name, false); } else { //parent is null, call BootstrapClassLoader to load c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { //If the loading is still not successful, call your own findClass to load c = findClass(name); } } if (resolve) { resolveClass(c); } return c; } From the above code, we can see that the general process of loading a class is the same as the example I gave before. When we want to implement a custom class, we only need to implement the findClass method.
Why use this parent delegation model?
The first reason is that this can avoid repeated loading. When the father has loaded the class, there is no need for the child ClassLoader to load it again.
The second reason is to consider security factors . Let’s imagine that if we do not use this delegate mode, we can dynamically replace the defined types in the Java core API at any time, which will pose a very big security risk. The parent delegate method can avoid this situation, because the String is already loaded at startup, so the user-defined class cannot load a custom ClassLoader.
The above is a brief introduction to the loading mechanism of ClassLoader . Next, I have to explain another class related to ClassLoader, that is, the Class class. Each class file loaded by ClassLoader will eventually be referenced by the programmer as an instance of the Class class. We can treat the Class class as a template of the ordinary class. The JVM generates corresponding instances based on this template and is finally used by the programmer.
We see that there is a static method forName in the Class class. This method is the same as the loadClass method in ClassLoader. It is used to load class, but the two have different functions.
Class<?> loadClass(String name)
Class<?> loadClass(String name, boolean resolve)
We see the above two method declarations. The second parameter of the second method is used to set whether to connect the class when loading the class. If true, it will be connected, otherwise it will not be connected.
Speaking of connection, I have to explain it here. When loading a class by JVM, it is necessary to go through three steps: loading, connecting, and initializing. Loading means finding the corresponding class file, reading it into the JVM, and initializing it needs to be discussed, the most important thing is to talk about connection.
The connection is divided into three steps. The first step is to verify whether the class meets the specifications. The second step is preparation. It is to allocate memory for the class variables and set the default initial value. The third step is explanation. This step is optional. According to the second parameter of the loadClass method above, it is determined whether an explanation is needed. The so-called explanation is based on the definition of the book "In-depth JVM" which is to find the corresponding entity based on the symbol references in the class, and then replace the symbol reference with a direct reference. It's a bit profound, haha, I won't explain it here. If you want to know more, please read "In-depth JVM". Haha, if you continue to explain it step by step, you won't know when it will be finished.
Let's take a look at the loadClass method with two parameters. In the JAVA API document, the definition of this method is protected, which means that the method is protected, and the method that users should really use is the one with one parameter. The loadclass method of one parameter actually calls the method with two parameters, and the second parameter defaults to false. Therefore, it can be seen here that the loading class through loadClass is actually not explained when loading, so the class will not be initialized. The forName method of the Class class is the opposite. When loading with forName, Class will be explained and initialized. ForName also has another version of the method, which can set whether to initialize and set ClassLoader. I won't talk about it more here.
I wonder if the explanation of these two loading methods is clear enough. Let’s give an example here. For example, when loading JDBC DRIVER , we use the forName instead of the loadClass method of ClassLoader when loading the JDBC driver? We know that the JDBC driver is through the DriverManager and must be registered in the DriverManager. If the driver class is not initialized, it cannot be registered in the DriverManager. Therefore, forName must be used instead of loadClass.
Through ClassLoader, we can customize the class loader and customize the loading method we need, such as loading from the network, loading from other formats of files, etc. In fact, there are still many things that have not been mentioned in ClassLoader, such as some implementations inside ClassLoader, etc.
Through this article, the editor hopes that everyone will have some understanding of the ClassLoader mechanism, thank you for your support!