1.类加载器简介
类加载器负责在运行时将 Java 类动态加载到 JVM (Java 虚拟机)。它们也是 JRE(Java 运行时环境)的一部分。因此,借助类加载器,JVM 无需了解底层文件或文件系统即可运行 Java 程序。
此外,这些 Java 类不会一次全部加载到内存中,而是在应用程序需要它们时加载。这就是类加载器发挥作用的地方。他们负责将类加载到内存中。
在本教程中,我们将讨论不同类型的内置类加载器及其工作方式。然后我们将介绍我们自己的自定义实现。
2.内置类加载器的类型
让我们从学习如何使用各种类加载器加载不同的类开始:
public void printClassLoaders() throws ClassNotFoundException {
System.out.println("Classloader of this class:"
+ PrintClassLoader.class.getClassLoader());
System.out.println("Classloader of Logging:"
+ Logging.class.getClassLoader());
System.out.println("Classloader of ArrayList:"
+ ArrayList.class.getClassLoader());
}
执行时,上述方法打印:
Class loader of this class:sun.misc.Launcher$AppClassLoader@18b4aac2
Class loader of Logging:sun.misc.Launcher$ExtClassLoader@3caeaf62
Class loader of ArrayList:null
正如我们所见,这里有三种不同的类加载器:应用程序、扩展程序和引导程序(显示为 null)。
应用程序类加载器加载包含示例方法的类。应用程序或系统类加载器在类路径中加载我们自己的文件。
接下来,扩展类加载器加载Logging类。扩展类加载器加载作为标准核心 Java 类的扩展的类。
最后,引导类加载器加载ArrayList类。引导程序或原始类加载器是所有其他类加载器的父级。
但是,我们可以看到,对于ArrayList,它在输出中显示为null 。这是因为引导类加载器是用本机代码而不是 Java 编写的,因此它不会显示为 Java 类。因此,引导类加载器的行为在不同的 JVM 中会有所不同。
现在让我们更详细地讨论这些类加载器。
(1)引导类加载器
Java 类由java.lang.ClassLoader的实例加载。但是,类加载器本身就是类。所以问题是,谁加载java.lang.ClassLoader本身?
这就是引导程序或原始类加载器发挥作用的地方。
它主要负责加载 JDK 内部类,通常是rt.jar和其他位于$JAVA_HOME/jre/lib目录下的核心库。此外,Bootstrap 类加载器充当所有其他ClassLoader实例的父级。
这个引导类加载器是核心 JVM 的一部分,并且是用本机代码编写的,如上面的示例中所指出的。不同的平台可能有这个特定类加载器的不同实现。
(2)扩展类加载器
扩展类加载器是引导类加载器的子类,负责加载标准核心 Java 类的扩展,以便平台上运行的所有应用程序都可以使用它们。
扩展类加载器从 JDK 扩展目录加载,通常是$JAVA_HOME/lib/ext目录,或java.ext.dirs系统属性中提到的任何其他目录。
(3)系统类加载器
另一方面,系统或应用程序类加载器负责将所有应用程序的类加载到 JVM 中。它加载在类路径环境变量、-classpath或-cp命令行选项中找到的文件。它也是扩展类加载器的子类。
3. 类加载器是如何工作的?
类加载器是 Java 运行时环境的一部分。当 JVM 请求一个类时,类加载器会尝试定位该类并使用完全限定的类名将类定义加载到运行时中。
java.lang.ClassLoader.loadClass()方法负责将类定义加载到运行时。它尝试根据完全限定名称加载类。
如果该类尚未加载,它将请求委托给父类加载器。这个过程递归地发生。
最终,如果父类加载器没有找到该类,那么子类将调用java.net.URLClassLoader.findClass()方法在文件系统本身中查找类。