Java 类加载过程分为:加载 → 验证 → 准备 → 解析 → 初始化,其中前四步称为"连接(Linking)"。
文章目录
- 一、每一步JVM都在做什么?
-
-
- [1. 加载(Loading)------ 把字节码"搬进 JVM"](#1. 加载(Loading)—— 把字节码“搬进 JVM”)
- [2. 验证(Verification)------ JVM 的"安检门"](#2. 验证(Verification)—— JVM 的“安检门”)
- [3. 准备(Preparation)------ 给"类变量"分配空间](#3. 准备(Preparation)—— 给“类变量”分配空间)
- [4.解析(Resolution)------ 把"符号引用"变成"直接引用"](#4.解析(Resolution)—— 把“符号引用”变成“直接引用”)
- [5. 初始化(Initialization)------ 真正执行 Java 代码](#5. 初始化(Initialization)—— 真正执行 Java 代码)
-
- 二、类什么时候会被初始化?
- [三、 父子类加载顺序(常考)](#三、 父子类加载顺序(常考))
- 四、类加载器和双亲委派机制
一、每一步JVM都在做什么?
1. 加载(Loading)------ 把字节码"搬进 JVM"
JVM 做的事:
-
根据类的全限定名
-
找到
.class文件 -
读入方法区(元空间)
-
创建
Class对象
来源可能是:
-
本地 class 文件
-
jar 包
-
网络
-
动态生成(代理类)
⚠️ 这一步 还没执行任何 Java 代码
2. 验证(Verification)------ JVM 的"安检门"
目的:保证字节码是合法、安全的
主要验证内容:
-
文件格式验证
-
元数据验证
-
字节码验证
-
符号引用验证
如果失败:
java
java.lang.VerifyError
3. 准备(Preparation)------ 给"类变量"分配空间
关键点(高频考):
-
只给 static 变量 分配内存
-
赋 默认值
java
static int a = 10;
准备阶段结果:
java
a = 0
4.解析(Resolution)------ 把"符号引用"变成"直接引用"
-
常量池里的符号引用
-
转成内存地址
比如:
-
类
-
方法
-
字段
⚠️ 有些 JVM 会 延迟到真正使用时才解析
5. 初始化(Initialization)------ 真正执行 Java 代码
这是 唯一会执行 Java 代码的阶段。
做的事:
-
执行
<clinit>方法-
static 变量显式赋值
-
static 代码块
-
示例:
java
static int a = 10;
static {
a = 20;
}
初始化后:
java
a = 20
二、类什么时候会被初始化?
会触发初始化的场景:
-
new对象 -
访问静态变量(非 final)
-
调用静态方法
-
反射
-
初始化子类,父类先初始化
-
JVM 启动时的主类
不会触发初始化的场景(陷阱)
java
public static final int A = 10;
-
编译期常量
-
不会触发类初始化
三、 父子类加载顺序(常考)
java
class Parent {
static {
System.out.println("Parent");
}
}
class Child extends Parent {
static {
System.out.println("Child");
}
}
java
new Child();
输出:
Parent
Child
规则:
先父后子,静态优先
四、类加载器和双亲委派机制
虽然不是"过程的一步",但几乎必追。
类加载器层级:
-
Bootstrap
-
Extension
-
Application
-
自定义
双亲委派作用:
-
防止核心类被篡改
-
保证类唯一性