类加载器
承灿 2023/10/20
Java 的类加载机制是 Java 虚拟机(JVM)用来加载类文件到内存中的方法。它是 Java 实现动态加载代码的基础,也是 Java 运行时环境的一部分。以下是类加载机制的主要组成部分和过程,以及一个具体的示例。
# 类加载的时机
在 Java 中,并非所有的类都在 Java 程序启动时一次性加载,而是根据需要,采用动态加载的方式。类在首次主动使用时才会加载。主动使用包括以下几种情况:
- 创建类的实例。
- 访问类或接口的静态变量,或者对该静态变量赋值。
- 调用类的静态方法。
- 使用反射方式来强制创建某个类或接口对应的 java.lang.Class 对象。
- 初始化子类时,如果发现父类还没有进行过初始化,则需要先触发父类的初始化。
- 作为程序的入口起点类(即包含 main 方法的类)。
# 类加载的过程
类加载过程主要分为加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)和初始化(Initialization)五个阶段。
- 加载(Loading): 在这个阶段,JVM 会查找 class 文件并创建一个代表这个类的 java.lang.Class 对象。加载是类加载过程的一个具体实现阶段,完成以下三件事情:
- 通过类的全限定名来获取此类的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这些数据的访问入口。
- 验证(Verification): 这一步确保加载的类满足 JVM 的约束条件,是正确的字节码文件。这个过程包括对类文件的格式验证、元数据验证、字节码验证、符号引用验证等。
- 准备(Preparation): 在准备阶段,JVM 为类的静态变量分配内存,并设置默认初始值。
- 解析(Resolution): 这个阶段涉及将类、接口、字段和方法的符号引用替换为直接引用的过程。
- 初始化(Initialization): 这是类加载的最后阶段,在此阶段,JVM 执行类的初始化语句,这包括静态字段的赋值和静态代码块的执行。初始化阶段是执行类构造器
<clinit>()
方法的过程。
# 类加载器
在 JVM 中存在多种类加载器,主要有:
- Bootstrap 类加载器(启动类加载器): 它是虚拟机的一部分,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。
- Extension 类加载器(扩展类加载器): 它由 sun.misc.Launcher$ExtClassLoader 实现。它负责加载 JAVA_HOME/lib/ext 目录中的,或者被 java.ext.dirs 系统变量指定的路径中的所有类库。
- Application 类加载器(应用类加载器): 它由 sun.misc.Launcher$AppClassLoader 实现。它负责加载用户类路径(Classpath)上所指定的类库,开发者可以直接使用这个类加载器。
- 自定义类加载器: Java 也允许开发者实现自己的类加载器,以满足某些特殊需求。
# 示例
假设我们有一个简单的 Java 类,名为 "Example",位于包 "com.demo" 中。当我们尝试创建这个类的一个实例时,类加载过程将会启动:
javaCopy codepackage com.demo;
public class Example {
static {
System.out.println("Static block of Example class.");
}
public Example() {
System.out.println("Creating an instance of Example class.");
}
}
// 在另一个类中,比如应用的 main 方法中:
public class Main {
public static void main(String[] args) {
Example example = new Example(); // 这里将触发 Example 类的加载、验证、准备、解析和初始化
}
}
在这个例子中,当 JVM 在 main 方法中遇到 "new Example()" 时,它会为 "com.demo.Example" 类执行加载过程。首先,它会尝试找到 "com.demo.Example" 类的字节码,然后进行验证,确保字节码文件的完整性和安全性。接着,在准备阶段,JVM 为类的静态变量分配内存。在解析阶段,它会处理类的各种符号引用,将这些引用替换为实际引用。最后,在初始化阶段,静态块中的代码将会被执行。
这个过程是由类加载器和 JVM 协同完成的,对于开发者来说,这个过程是透明的。