类加载器
类加载器(Class Loader)是 Java 虚拟机(JVM)的一部分,它的作用是将类的字节码文件(.class
文件)从磁盘或其他来源加载到 JVM 中。类加载器负责查找和加载类的字节码文件,并将其转化为 Class 对象。
类加载器从 JDK 1.0 就出现了,最初只是为了满足 Java Applet
(已经被淘汰) 的需要。后来,慢慢成为 Java 程序中的一个重要组成部分,赋予了 Java 类可以被动态加载到 JVM 中并执行的能力。
根据官方 API 文档的介绍:
类加载器是一个负责加载类的对象。
ClassLoader
是一个抽象类。给定类的二进制名称,类加载器应尝试定位或生成构成类定义的数据。典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的“类文件”。
每个 Java 类都有一个引用指向加载它的
ClassLoader
。但数组类不是通过ClassLoader
创建的,而是 JVM 在需要的时候自动创建的,数组类通过getClassLoader()
方法获取ClassLoader
的时候和该数组的元素类型的ClassLoader
是一致的。
-
类加载器是一个负责加载类的对象,用于实现类加载过程中的加载这一步。
-
每个 Java 类都有一个引用指向加载它的
ClassLoader
。 -
数组类不是通过
ClassLoader
创建的(数组类没有对应的二进制字节流),是由 JVM 直接生成的。
组成部分
在 Java 中,类加载器主要有三个层次:
-
启动类加载器(Bootstrap ClassLoader):这是最基础的类加载器,由 C++ 实现,通常表示为 null,并且没有父级,负责加载扩展目录下的 jar 包和系统类路径下的核心库(
%JAVA_HOME%/lib
目录下的rt.jar
、resources.jar
、charsets.jar
等 jar 包和类)以及被-Xbootclasspath
参数指定的路径下的所有类。rt.jar
:rt 代表“RunTime”,rt.jar
是 Java 基础类库,包含 Java doc 里面看到的所有的类的类文件。也就是说,我们常用内置库java.xxx.*
都在里面,比如java.util.*
、java.io.*
、java.nio.*
、java.lang.*
、java.sql.*
、java.math.*
。 -
扩展类加载器(Extension ClassLoader):由 Java 实现,负责加载 Java 默认扩展目录下的 jar 包(
%JRE_HOME%/lib/ext
目录下的 jar 包和类以及被java.ext.dirs
系统变量所指定的路径下的所有类)。 -
系统类加载器(System/App ClassLoader):也称为应用程序类加载器,由 Java 实现,负责加载用户类路径(
classpath
)下的所有 jar 包和类。
除了这三个内置的类加载器外,还可以自定义类加载器,通过继承 java.lang.ClassLoader
类的方式实现,以满足特殊的需求。例如,可以通过自定义类加载器来加载网络上的类,或者从数据库中加载类。
对于任意一个类,都需要由它的类加载器和这个类本身一同确定其在 JVM 中的唯一性。也就是说,如果两个类的加载器不同,即使两个类来源于同一个字节码文件,那这两个类就必定不相等(比如两个类的 Class 对象不 equals
)。
ClassLoader
除了 BootstrapClassLoader
是 JVM 自身的一部分之外,其他所有的类加载器都是在 JVM 外部实现的,并且全都继承自 ClassLoader
抽象类。这样做的好处是用户可以自定义类加载器,以便让应用程序自己决定如何去获取所需的类。
每个 ClassLoader
可以通过 getParent()
获取其父 ClassLoader
,如果获取到 ClassLoader
为 null
的话,那么该类是通过 BootstrapClassLoader
加载的。由于 BootstrapClassLoader
由 C++ 实现,由于这个 C++ 实现的类加载器在 Java 中是没有与之对应的类的,所以拿到的结果是 null。
public abstract class ClassLoader {
...
// 父加载器
private final ClassLoader parent;
@CallerSensitive
public final ClassLoader getParent() {
//...
}
...