ClassLoader在类创建过程中的作用

107 阅读3分钟

ClassLoader什么时候被加载的

ClassLoader 是在Java虚拟机(JVM)启动时被加载的。具体来说,当JVM启动时,它会加载一些核心类,这些类是Java运行时环境(JRE)的一部分,包括 java.lang.ClassLoader 类。

这个过程通常在JVM启动时通过引导类加载器(Bootstrap ClassLoader)完成。引导类加载器是JVM启动时第一个被加载的类加载器,它负责加载Java核心库(如 rt.jar)中的类。

引导类加载器使用本地代码实现,它直接从本地文件系统加载类。引导类加载器加载的类包括 java.lang.Object、java.lang.ClassLoader、java.lang.String 等核心类。

一旦引导类加载器加载了 ClassLoader 类,其他类加载器(如扩展类加载器 ExtClassLoader 和应用程序类加载器 AppClassLoader)就可以被加载和初始化。这些类加载器通常在JVM启动后通过调用 ClassLoader 的构造方法来创建。

因此,ClassLoader 是在JVM启动时被加载的,具体来说,是在引导类加载器加载 java.lang.ClassLoader 类时被加载的

Class中的ClassLoader属性

// 这里是Class类的构造函数,可以看到,这里把类加载器传入了
private Class(ClassLoader loader, Class<?> arrayComponentType) {
        // Initialize final field for classLoader.  The initialization value of non-null
        // prevents future JIT optimizations from assuming this final field is null.
        classLoader = loader;
        componentType = arrayComponentType;
    }

我们被class关键字修饰的对象,会生成一个Class对象,也就是我们使用平时使用的类对象。这些类对象内部就包含了加载该类的类加载器。

创建对象与类加载器的关系

  • 运行程序前,进行编译。把你的java文件,编译成.class结尾的字节码文件
  • java程序启动时,jvm先加载java的核心类,其中就包括类加载器ClassLoader(ClassLoader是一个抽象类,它有着诸多的实现)
  • 然后再由ClassLoader加载编译好的.class文件,并且生成Class对象。生成Class对象时,ClassLoader就被Class对象保存了
  • 当我们使用 new 关键字创建一个对象时,JVM会从类加载器加载的类中创建该对象

创建一个类与类加载器的关系

  • 查找类:
    • JVM首先会查找该类的 Class 对象。如果该类已经被加载,那么JVM会直接返回该类的 Class 对象。如果该类没有被加载,那么JVM会使用该类的类加载器来加载该类。
  • 确定类加载器:
    • 如果该类是由应用程序类加载器(AppClassLoader)加载的,那么JVM会使用应用程序类加载器来创建对象。
    • 如果该类是由其他类加载器加载的,那么JVM会使用该类加载器来创建对象。
  • 创建对象:
    • 一旦确定了类加载器,JVM就会使用该类加载器来创建对象。具体来说,JVM会为对象分配内存,并调用类的构造方法来初始化对象的状态。

一点题外话

思考一下,我们如果在程序内调用Class.forName()去获取一个Class对象,使用的哪个类加载器?

结论:哪个方法调用了Class.forName(),就用这个方法的类的类加载器。

public class ClassLoaderTest {

    public static void main(String[] args) {
        // 获取当前线程上下文中的CLassLoader
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        System.out.println(loader);

        // 获取当前类ClassLoaderTest的ClassLoader
        ClassLoader classLoaderTest = ClassLoaderTest.class.getClassLoader();

        try {
            // 通过Class.forName加载一下项目中的类
            Class<?> myClass = Class.forName("com.example.javatest.entity.Person");
            System.out.println(myClass.getClassLoader());
            // 比较当前线程上下文中的ClassLoader和Person类的ClassLoader是否相同
            System.out.println(loader==myClass.getClassLoader());
            System.out.println(loader==classLoaderTest);
            System.out.println(myClass.getClassLoader()==classLoaderTest);
            // 以上的结果都为true
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

}

Thread.currentThread().getContextClassLoader()和Class.forName()内部的核心方法,其实都是Reflection.getCallerClass()方法 所以他们能得到一致的结果