双亲委派核心逻辑即ClassLoader.loadClass方法的三步执行链:先查findLoadedClass缓存,再委托parent.loadClass(parent为null时调用findBootstrapClassOrNull),最后父级全失败才调用findClass自行加载。

直接看 ClassLoader.loadClass 方法的逻辑,是最高效的学习路径。它不长,但完整体现了双亲委派的核心流程,理解它就等于抓住了整个模型的骨架。
从 loadClass 源码切入
JDK 8 中 `java.lang.ClassLoader.loadClass(String, boolean)` 是关键入口,它的执行顺序就是双亲委派的执行顺序: - 先查缓存:调用 `findLoadedClass(name)`,避免重复加载 - 再向上委托:如果 `parent != null`,递归调用 `parent.loadClass(name, false)` - 到顶后交由 Bootstrap:当 `parent == null` 时,调用 `findBootstrapClassOrNull(name)`(底层 C++ 实现,Java 层不可见) - 父级全部失败才自己动手:只有 `c == null` 时,才调用 `findClass(name)`,由子类实现具体字节码读取逻辑这个方法里没有魔法,只有清晰的“先问爹、再问爷、最后自己干”的判断链。
重点盯住三个字段和方法
- `parent` 字段:不是继承关系,而是组合关系。每个 `ClassLoader` 实例持有一个父加载器引用,构成委托链 - `findClass(String)`:模板方法,必须由自定义加载器重写,负责从文件、网络或数据库等来源获取 `.class` 字节流 - `defineClass(byte[], ...)`:final 方法,把字节流真正转成 `Class>` 对象,不可覆盖,保证安全性注意:loadClass 负责流程控制,findClass 负责资源定位,defineClass 负责类型生成——三者分工明确。
验证逻辑比背概念更有效
写一段小代码验证流程: ```java System.out.println(String.class.getClassLoader()); // 输出 null → Bootstrap 加载 System.out.println(File.class.getClassLoader()); // 通常是 AppClassLoader(JDK9+ 可能是 Platform) System.out.println(YourClass.class.getClassLoader().getParent()); // 看到 ExtClassLoader ``` 再配合打印 `sun.misc.Launcher.getBootstrapClassPath().getURLs()`、`System.getProperty("java.ext.dirs")`、`System.getProperty("java.class.path")`,就能直观看到三层加载器各自管哪块路径。别跳过“为什么这样设计”
- 为什么 `parent` 为 null 就代表 Bootstrap?因为它是 JVM 内置的,Java 层无法实例化,用 null 做语义标记 - 为什么 `findClass` 抛 `ClassNotFoundException` 而不直接返回 null?强制子类必须明确处理“找不到”的语义 - 为什么 `loadClass` 加了 `synchronized(getClassLoadingLock(name))`?防止多线程并发加载同一类时产生多个 `Class` 实例这些细节不是为了炫技,而是保障类唯一性和 JVM 安全性的刚性约束。
立即学习“Java免费学习笔记(深入)”;
不复杂但容易忽略


















