题目
简述Java类加载的基本过程
信息
- 类型:问答
- 难度:⭐
考点
类加载过程,类加载器,双亲委派模型
快速回答
Java类加载分为三个主要阶段:
- 加载:查找并加载类的二进制字节码
- 链接:包含验证、准备、解析三个子阶段
- 初始化:执行类构造器<clinit>()方法
类加载器采用双亲委派模型,优先委派父加载器处理。
解析
一、类加载基本过程
当JVM首次使用类时,会触发以下完整流程:
- 加载(Loading)
- 通过全限定名获取类的二进制字节流
- 将字节流转化为方法区的运行时数据结构
- 在堆中生成代表该类的Class对象
- 链接(Linking)
- 验证(Verify):检查字节码安全性(文件格式、元数据等)
- 准备(Prepare):为静态变量分配内存并设置默认值(如int=0)
- 解析(Resolve):将符号引用转换为直接引用
- 初始化(Initialization)
- 执行类构造器<clinit>()方法
- 为静态变量赋程序设定的初始值
- 执行静态代码块
二、代码示例
public class LoadDemo {
static int value = 10; // 准备阶段value=0,初始化阶段变为10
static { System.out.println("静态代码块执行"); }
public static void main(String[] args) {
System.out.println(LoadDemo.value);
}
}
输出顺序:
1. 加载LoadDemo类 → 2. 链接阶段(value初始化为0) → 3. 初始化阶段(value赋值为10,打印"静态代码块执行") → 4. main方法打印10
三、双亲委派模型
类加载器层级结构:
- Bootstrap ClassLoader:加载JRE/lib核心库(C++实现)
- Extension ClassLoader:加载JRE/lib/ext扩展库
- Application ClassLoader:加载classpath用户类
工作流程:
1. 收到加载请求后,先委托父加载器处理
2. 父加载器无法完成时(在自己的搜索范围找不到类),子加载器才尝试加载
四、最佳实践
- 避免自定义类加载器破坏双亲委派(特殊场景如热部署除外)
- 静态变量初始化避免循环依赖:
// 错误示例 class A { static int val = B.val + 1; } class B { static int val = A.val + 1; } // 导致初始化死锁
五、常见错误
ClassNotFoundException:类加载器在指定路径找不到类NoClassDefFoundError:编译时存在类,但运行时链接失败- 混淆「准备」和「初始化」阶段:误认为准备阶段就会执行静态赋值
六、扩展知识
- 触发初始化时机:new实例、访问静态变量/方法、反射调用、初始化子类等
- 不会触发初始化:通过子类引用父类静态字段、数组定义(如
MyClass[] arr = new MyClass[10])