Java类加载机制的核心是“按需加载”和“层级委托”,双亲委派模型是其默认执行规则,由ClassLoader.loadClass方法源码固化:先委托父加载器,父无法加载时才调用findClass自加载;类生命周期严格分为加载、链接(验证/准备/解析)、初始化三阶段,且仅首次主动使用触发初始化。

Java 类加载机制的核心在于“按需加载”和“层级委托”,而双亲委派模型正是这一机制的执行规则。它不是语法层面的强制约定,而是类加载器 loadClass 方法的默认实现逻辑——由 JVM 规范约束、由 ClassLoader 基类源码固化。
类加载的三阶段本质
一个类从 .class 文件到可运行状态,必须完成三个不可跳过、严格串行的阶段:
-
加载(Loading):查找字节码(磁盘/网络/JAR)、读入内存、生成
java.lang.Class对象,并在方法区(或元空间)建立运行时数据结构(如instanceKlass)。这一步不校验、不分配静态变量内存,只做“搬运+注册”。 -
链接(Linking):分三步进行:
✓ 验证:检查字节码是否符合 JVM 规范(魔数、版本、结构合法性、安全限制);
✓ 准备:为类的静态变量分配内存并设默认值(如int设为 0,Object设为null),不执行任何赋值语句;
✓ 解析:将常量池中的符号引用(如类名、字段名、方法签名)替换为直接引用(内存地址或句柄)。 -
初始化(Initialization):真正执行 Java 代码——执行
<clinit>方法,即静态变量显式赋值 + 静态代码块,且只执行一次。只有当首次主动使用该类(如 new 实例、调用静态方法、反射访问等)才会触发。
双亲委派的源码级运作流程
双亲委派不是 JVM 的“魔法”,而是 ClassLoader.loadClass(String, boolean) 方法的标准实现逻辑。它的关键行为写死在 java.lang.ClassLoader 的源码中:
- 收到类加载请求后,先调用
findLoadedClass(name)检查是否已加载,避免重复。 - 若未加载,且当前加载器有父加载器(
parent != null),则递归调用parent.loadClass(name, false)——注意:这是向上委托,不是向下查找。 - 若父加载器返回
null(即无法加载),才调用自身的findClass(name)尝试加载(此方法默认抛异常,需子类重写)。 - 最后,若成功得到
Class对象,根据参数决定是否触发链接(resolve == true时调用resolveClass())。
整个链条是:Application → Platform(JDK9+)或 Extension(JDK8-)→ Bootstrap。Bootstrap 是 C++ 实现、无 Java 父类,所以它的 parent 为 null,自然终止委托。
立即学习“Java免费学习笔记(深入)”;
为什么必须打破双亲委派?典型场景与破法
双亲委派保障了核心类(如 java.lang.Object)的唯一性和安全性,但在以下场景中必须绕过它:
-
热部署与模块隔离:如 Tomcat 为每个 Web 应用创建独立的
WebAppClassLoader,它重写loadClass,优先用自己的findClass加载/WEB-INF/classes和/WEB-INF/lib,失败后再委托父加载器——避免应用间类冲突,也防止 Web 应用覆盖 JDK 类。 - OSGi 动态模块系统:每个 Bundle 拥有自己类加载器,支持细粒度导出/导入包,依赖关系由元数据控制,完全脱离双亲链。
-
自定义类来源:从网络、数据库、加密文件加载字节码时,必须继承
ClassLoader并重写findClass,因为父加载器根本找不到这些路径。
破法本质只有一条:重写 loadClass 方法,改变“先委托、再自加载”的顺序,改为“先自加载、再委托”或“按策略混合委托”。
类加载器的层级与获取方式
JVM 内置加载器形成明确层级,但并非所有都可通过 Java 代码直接引用:
-
Bootstrap ClassLoader:C++ 实现,加载
rt.jar等核心类(java.*),String.class.getClassLoader()返回null。 -
Platform ClassLoader(JDK9+)或 Extension ClassLoader(JDK8-):加载
lib/ext或平台模块,可通过ClassLoader.getPlatformClassLoader()获取。 -
System ClassLoader:即应用类加载器,加载
-cp指定路径,ClassLoader.getSystemClassLoader()可获取,也是自定义加载器的默认父加载器。
所有用户自定义加载器,都应以 System 或 Platform 加载器为父,以保持对标准库的可见性,除非刻意隔离。


















