最近学了反射机制,解除了有关类加载器的一些概念,有些模糊,所以去外网找了一些资料。
本文转载外网文章。
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
从上面程序的运行结果可以看出,这里有三种不同的类加载器:应用类加载器(application class loader)、扩展类加载器(extension class loader)和启动类加载器(bootstrap class loader,就是上面运行结果显示为 null的)。
应用类加载器加载在classpath中的我们自己写的文件。
扩展类加载器加载核心 Java 类的扩展类(ext/*.jar包下的类)。
启动类加载器是所有其他类加载器的parent(rt.jar包下的类)。
但是,我们可以看到,对于ArrayList,它在输出中显示为null 。这是因为启动加载器是用本机代码(native code)而不是 Java 编写的,因此它不会显示为 Java 类。因此,引导类加载器的行为在不同的 JVM 中会有所不同。
现在让我们更详细地讨论这些类加载器。
2.1。启动类加载器
Java 类由java.lang.ClassLoader的对象/实例加载。但是类加载器本身就是一个类。所以现在的问题是,谁加载java.lang.ClassLoader这个类呢?
答案是:启动类加载器。
它主要负责加载 JDK 内部类,通常是rt.jar和其他位于$JAVA_HOME/jre/lib目录下的核心类库。此外,启动类加载器是所有其他ClassLoader实例的parent。
这个启动类加载器是核心 JVM 的一部分,并且是用本机代码(native codde)编写的。根据上面的代码我们可以看到,不同的平台可能有这个特定类加载器的不同实现。
2.2. 扩展类加载器
扩展类加载器是启动类加载器的子类,负责加载核心 Java 类的扩展,以便平台上运行的所有应用程序都可以使用它们。
扩展类加载器从 JDK 扩展目录加载,通常是$JAVA_HOME/lib/ext目录,或java.ext.dirs系统属性中存在的任何其他目录。
2.3.应用/系统类加载器(application class loader)
应用类加载器负责将所有应用程序级别的类加载到 JVM 中。它会加载在类路径环境变量(-classpath或-cp命令行选项)中找到的文件。它也是扩展类加载器的子类。
3. 类加载器是如何工作的?
类加载器是 Java 运行时环境的一部分。当 JVM 请求访问一个类时,类加载器会尝试定位该类并使用类的全名将类加载到运行时环境。
java.lang.ClassLoader.loadClass()方法负责将类定义加载到运行时环境。它尝试根据类的全名称加载类。
如果该类还没有被加载过,它会将请求委托给父类加载器。这个过程是递归发生的。
最终,如果父类加载器没有找到该类,那么子类加载器将调用java.net.URLClassLoader.findClass()方法在文件系统本身中查找类。
如果最后一个子类加载器也无法加载该类,它会抛出java.lang.NoClassDefFoundError或java.lang.ClassNotFoundException。
让我们看一个抛出ClassNotFoundException时的输出示例:
java.lang.ClassNotFoundException: com.baeldung.classloader.SampleClassLoader
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
如果我们从调用java.lang.Class.forName()开始往后看,我们可以看到它首先尝试通过父类加载器加载类,然后再通过java.net.URLClassLoader.findClass()寻找这个类。
当它仍然找不到类时,它会抛出ClassNotFoundException。