Java类加载机制按需分阶段执行,含加载、验证、准备、解析、初始化五阶段;由Bootstrap、Extension、Application三类加载器构成双亲委派链,确保安全与唯一性。

Java 类加载机制是 JVM 运行代码的前提,它不是启动时一股脑全加载,而是按需、分阶段、有规则地把 .class 文件变成内存中可执行的类。核心在于“懒加载 + 双亲委派 + 三阶段推进”。
什么时候真正开始加载?
类不会在 JVM 启动时自动加载,只在首次主动使用时触发:
- 用
new创建实例(如new Student()) - 访问静态字段(非
public static final编译期常量),比如System.out.println(Config.URL) - 调用静态方法(如
StringUtils.isEmpty()) - 反射加载:
Class.forName("com.example.Service")(默认会初始化) - 初始化子类(父类先被加载并初始化)
声明但未使用不触发:写 Student s; 不加载;引用 final static String NAME = "A"; 也不加载(编译期已内联)。
加载过程分哪几步?
一个类从字节码到可用,要经过五个关键阶段,前三个由类加载器主导,后两个由 JVM 自动推进:
-
加载(Loading):根据全限定名找到 .class 字节流(本地文件、JAR、网络、动态生成等),在方法区生成类元数据结构,并在堆中创建对应的
java.lang.Class对象 - 验证(Verification):检查字节码是否合规(格式、语义、符号引用等),防止恶意或损坏代码破坏 JVM
-
准备(Preparation):为
static变量分配内存并设默认值(int → 0,Object → null);注意:final static常量在此阶段直接赋真实值 - 解析(Resolution):将类、字段、方法的符号引用(如字符串描述)转为内存中的直接引用(如指针或偏移量);部分解析可延迟到初始化后
-
初始化(Initialization):执行
方法——合并所有静态变量赋值和静态代码块,按顺序执行;父类优先于子类()
谁来加载?双亲委派怎么运作?
JVM 内置三类加载器,构成自上而下的委托链:
-
Bootstrap ClassLoader(C++ 实现):加载
rt.jar等核心类(java.lang.*、java.util.*),getClassLoader()返回null -
Extension ClassLoader:加载
jre/lib/ext或java.ext.dirs指定路径的扩展类 -
Application ClassLoader:加载
classpath下的用户类和第三方库,是默认加载器
双亲委派流程:当 AppClassLoader 收到加载请求,先调 getParent().loadClass() 委托给 ExtClassLoader;Ext 再委托给 Bootstrap;只有上级全部返回 ClassNotFoundException,当前加载器才调 findClass() 自己找字节码并 defineClass() 注册。
这样既避免重复加载(如两个加载器都去加载 String),也防止用户类篡改核心类型(自定义 java.lang.Object 会被 Bootstrap 拦住)。
自定义类加载器要注意什么?
如果要从数据库、加密包或网络加载类,应继承 ClassLoader,只重写 findClass(),不要动 loadClass():
- 正确做法:在
findClass()中读取字节流 → 调defineClass()→ 返回 Class - 错误做法:重写
loadClass()并直接defineClass()→ 绕过委派 → 可能加载两份String→LinkageError致 JVM 崩溃
需要打破委派的场景(如 Tomcat 隔离 Web 应用、JDBC 加载驱动),可通过线程上下文类加载器(Thread.currentThread().setContextClassLoader())切换,而非破坏 loadClass() 流程。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











