Java 类的加载过程
史博辉 2024-06-22 15:08:00 java
Java 类的加载过程是一个复杂且系统化的过程,主要包括以下三个步骤:加载(Loading)、链接(Linking)和初始化(Initialization)。下面是详细的步骤说明:
# 1. 加载(Loading)
在这个阶段,Java 虚拟机(JVM)需要将类的二进制数据(字节码文件)读取到内存中,并且在内存中创建一个 java.lang.Class 对象代表这个类。加载阶段主要包括以下几个步骤:
- 寻找和导入类的二进制数据:JVM 通过类的全限定名从文件系统、网络、JAR 包等位置找到对应的字节码文件(.class 文件)。
- 创建 Class 对象:在内存中创建一个代表该类的 Class 对象。
# 2. 链接(Linking)
链接阶段将加载的类合并到 JVM 中,使之能够被执行。链接阶段又可以细分为三个子阶段:
# 验证(Verification)
验证阶段确保类的字节码符合 JVM 规范,并且没有安全威胁。主要包括:
- 文件格式验证:检查字节码文件格式是否符合规范。
- 元数据验证:检查类的元数据,例如类的继承关系、是否实现了所有抽象方法等。
- 字节码验证:通过数据流和控制流分析确保字节码逻辑是合法的。
- 符号引用验证:确保解析符号引用时能够正确找到对应的类、字段和方法。
# 准备(Preparation)
准备阶段为类的静态变量分配内存,并将其初始化为默认值(例如 0、null、false 等)。这个阶段不包括对静态变量的显式初始化,只设置默认值。
# 解析(Resolution)
解析阶段将常量池中的符号引用转换为直接引用。符号引用是一个以字符串形式描述的符号(如类名、方法名、字段名等),而直接引用是指向实际内存地址的指针或句柄。
# 3. 初始化(Initialization)
初始化阶段是执行类构造器 <clinit>()
方法的过程。类构造器 <clinit>()
方法是由编译器自动收集类中的所有类变量的显式赋值语句和静态代码块合并产生的。主要包括:
- 初始化静态变量:将静态变量初始化为指定的值。
- 执行静态代码块:执行类中的静态代码块。
- 在类初始化阶段,还会遵循类加载的顺序:
父类静态变量和静态代码块。
子类静态变量和静态代码块。
# 类加载器(ClassLoader)
JVM 使用类加载器(ClassLoader)加载类。主要的类加载器有:
- Bootstrap ClassLoader:引导类加载器,加载 Java 核心库(rt.jar)。
- Extension ClassLoader:扩展类加载器,加载扩展库(ext 目录下的 JAR 文件)。
- Application ClassLoader:应用类加载器,加载应用程序的类路径(classpath)下的类。
# 类的加载过程示例
public class ClassLoadingExample {
static {
System.out.println("ClassLoadingExample static block");
}
public static void main(String[] args) {
System.out.println("Main method");
new A();
}
}
class A {
static {
System.out.println("Class A static block");
}
{
System.out.println("Class A instance block");
}
public A() {
System.out.println("Class A constructor");
}
}
# 详细分析
加载 ClassLoadingExample 类:
- 加载 ClassLoadingExample 类时,首先找到对应的字节码文件并加载到内存中,创建 Class 对象。
链接 ClassLoadingExample 类:
- 验证、准备和解析 ClassLoadingExample 类。
初始化 ClassLoadingExample 类:
- 执行 ClassLoadingExample 类的静态代码块,输出 ClassLoadingExample static block。
执行 main 方法:
- 输出 Main method。
加载 A 类:
- 加载 A 类时,找到对应的字节码文件并加载到内存中,创建 Class 对象。
链接 A 类:
- 验证、准备和解析 A 类。
初始化 A 类:
- 执行 A 类的静态代码块,输出 Class A static block。
创建 A 类的实例:
- 执行 A 类的实例初始化块,输出 Class A instance block。
- 调用 A 类的构造器,输出 Class A constructor。
以上过程展示了类加载、链接和初始化的详细步骤,以及实例初始化的过程。