类初始化和类加载
类初始化和类加载
一、类的生命周期
类从被加载到内存到被卸载,经历了以下几个阶段:
1 | 加载 → 验证 → 准备 → 解析 → 初始化 → 使用 → 卸载 |
其中:验证、准备和解析这三个阶段可以统称为连接。

二、类加载全过程
JVM 从 .class 文件到可以使用,大致分为 5 个阶段:
1 | 加载 → 验证 → 准备 → 解析 → 初始化 |
2.1 加载(Loading)
做什么:
- 根据类的全类名(如
com.xxx.User)找到.class文件 - 读取字节码
- 在内存中生成一个
Class对象(方法区)
关键点:
- 不一定从磁盘加载(也可以网络、动态生成)
- 由 类加载器 ClassLoader 完成
2.2 验证(Verification)
做什么:
- 校验
Class文件的字节流信息是否合法,保证不会危害 JVM
典型检查:
- 文件格式是否正确
- 是否符合 JVM 规范
- 是否有非法指令
可以理解为:安全检查
验证阶段主要由四个检验阶段组成:
- 文件格式验证(Class 文件格式检查)
- 元数据验证(字节码语义检查)
- 字节码验证(程序语义检查)
- 符号引用验证(类的正确性检查)
2.3 准备(Preparation)
做什么:
- 为 类变量(static变量)分配内存
- 设置 默认初始值
⚠️ 注意:
1 | static int a = 10; |
在准备阶段:
1 | a = 0 // 不是10! |
这里只是“分配 + 默认值”,不是赋值
2.4 解析(Resolution)
做什么:
- 将常量池中的 符号引用 → 直接引用
举例:
1 | User u = new User(); |
- 符号引用:字符串 “User”
- 解析后:内存地址
可以理解为:“找地址”
2.5 初始化(Initialization)🔥重点
做什么:
- 执行初始化方法
<clinit>() - 真正给 static 变量赋值
- 执行 static 代码块
例如:
1 | static int a = 10; |
初始化后:
1 | a = 20 |
三、类初始化
3.1 什么是类初始化?
就是执行:
1 | <clinit>() 方法 |
它由:
- 静态变量赋值
- static代码块
按顺序合并生成
3.2 类什么时候会初始化?(🔥)
会触发初始化的情况
- new对象
1 | new User(); |
- 访问类的静态变量(非final)
1 | User.a; |
- 调用静态方法
1 | User.test(); |
- 反射
1 | Class.forName("User"); |
-
初始化子类(会先初始化父类)
-
JVM启动时的主类
不会触发初始化
- 访问 static final 常量
1 | static final int a = 10; |
编译期已放入常量池
- 通过数组定义类
1 | User[] arr = new User[10]; |
只是创建数组
- 访问父类的静态变量(通过子类)
1 | Child.a; |
只初始化父类
四、类加载 vs 类初始化(核心区别)
| 对比点 | 类加载 | 类初始化 |
|---|---|---|
| 范围 | Loading ~ Resolution | Initialization |
| 是否执行代码 | ❌ 不执行 | ✅ 执行static代码 |
| static变量 | 默认值 | 赋真实值 |
| 是否必须执行 | 不一定 | 只有触发才执行 |
五、一个经典执行顺序例子🔥
1 | class A { |
执行:
1 | new B(); |
输出:
1 | A init |
说明:
- 先初始化父类
- 再初始化子类
六、面试高频总结
可以这样答👇:
JVM类的生命周期包括加载、验证、准备、解析、初始化、使用和卸载七个阶段。
JVM类加载分为五个阶段:加载、验证、准备、解析、初始化。
其中初始化阶段会执行类的<clinit>方法,对静态变量进行赋值并执行 static 代码块。
类初始化是在首次主动使用时触发,比如 new对象、访问静态变量、调用静态方法、反射等。
类加载只是把类加载进内存,而初始化才是真正执行类中的代码。