Java虚拟机类加载机制包含加载、验证、准备、解析、初始化五个强制顺序阶段,由类加载器协作执行:加载生成Class对象;验证保障字节码安全;准备分配静态变量默认值;解析替换符号引用为直接引用;初始化执行方法。

Java虚拟机类加载体系不是靠“看”懂的,而是靠拆解流程、对照规范、结合字节码来理解的。
核心在于抓住三个锚点:类加载的五个阶段不可跳过、每个阶段干了什么、谁来执行(类加载器)。脱离这三点去读图或看概览,容易停留在表面。
类加载的五个阶段必须按序发生
JVM规范强制规定了加载、验证、准备、解析、初始化这五步的顺序,但允许某些阶段部分重叠(如解析可能在初始化之后才完成,针对动态语言支持)。关键逻辑如下:
-
加载:找到
.class文件(或字节码流),生成java.lang.Class对象;不负责链接,也不执行代码 -
验证:检查字节码是否符合 JVM 规范,比如方法表结构、类型匹配、栈帧完整性;失败直接抛
VerifyError -
准备:为类的静态变量分配内存,并设为默认值(如
int为0,Object为null);注意:不执行static {}或赋值语句 -
解析:把常量池中的符号引用(如
"java/lang/Object")替换成直接引用(如内存地址或句柄);涉及类、字段、方法、接口调用等 -
初始化:真正执行类构造器
<clinit></clinit>方法,包括静态变量显式赋值和静态代码块;这是唯一允许用户代码介入的阶段
示例:
public static final int VERSION = 123;在准备阶段就设为123(因为是编译期常量),而public static int FLAG = compute();要等到初始化阶段才调用compute()。立即学习“Java免费学习笔记(深入)”;
sc-java-to-ts-interface下载将后端 Java 接口文档转换为前端 TypeScript 类型定义(interface / type)。输入:飞书文档、Spring Boot 接口文档、Swagger/OpenAPI、手写 API 文档等。输出:包含请求参数与响应结果的 .md 文件,含完整 JSDoc 注释。触发关键词:Java 转 TS、接口转换、生成前端类型、接口文档转 TypeScript、DTO/VO/BO 转 interface、后端文档转前端类型、生成接口类型、类型定义生成。
类加载器不是单个组件,而是一套协作模型
JVM 不会自己硬编码去读文件,所有加载行为都委托给类加载器。主流实现采用双亲委派模型,但本质是责任链 + 委托优先机制:
- 启动类加载器(Bootstrap ClassLoader):C++ 实现,加载
rt.jar等核心类,无 Java 对应对象 - 扩展类加载器(Extension ClassLoader):加载
jre/lib/ext下的类 - 应用程序类加载器(AppClassLoader):加载
-classpath指定路径的类 - 自定义类加载器:继承
ClassLoader,可打破委派(如热部署、OSGi、模块隔离)
双亲委派的意义在于安全与一致性:避免用户自己写一个
java.lang.String替换掉系统类。破坏委派常见于 JDBC(DriverManager需要加载不同厂商驱动)、Tomcat(每个 WebApp 隔离加载)、Spring Boot(LaunchedURLClassLoader加载 fat jar 内部 class)。
Class 文件是整个机制的输入源头
类加载的起点是二进制 .class 文件,它不是文本,而是严格排布的8 字节对齐二进制流,由 u1/u2/u4/u8 无符号数和 _info 表嵌套构成。例如:
-
magic固定为0xCAFEBABE -
minor_version和major_version决定能否被当前 JVM 加载(如 JDK 17 编译的 class 在 JDK 8 上无法运行) -
constant_pool_count后紧跟常量池数组,里面存着类名、方法名、字符串字面量等符号引用 -
access_flags、this_class、super_class、interfaces、fields、methods、attributes构成完整类定义骨架
工具建议:用
javap -v MyClass.class查看反编译结构;用xxd MyClass.class | head -20直接看十六进制头,验证 magic 和版本号。
类加载体系的价值不在“知道有哪几步”,而在于你能据此解释真实问题:
为什么 NoClassDefFoundError 和 ClassNotFoundException 行为不同?
为什么 static final String 能跨类内联,而 static String 不能?
为什么 Tomcat 要为每个应用创建独立的 WebAppClassLoader?
这些问题的答案,全藏在这五个阶段与三类加载器的交互细节里。


















