classloader.loadclass(string, boolean)是类加载入口,执行“查缓存→委托父加载器→兜底findclass→defineclass生成class对象”四步逻辑,parent字段承载双亲委派,findclass须调用defineclass才能完成加载。

直接看 ClassLoader.loadClass(String, boolean) 方法,它是整个类加载流程的入口和双亲委派的核心实现。重点不是逐行背代码,而是抓住“委托—检查—加载—解析”四步逻辑链,再结合关键字段理解行为边界。
从 loadClass 入口切入,理清执行主干
该方法是公开的、被 JVM 和用户代码频繁调用的加载入口。源码中清晰体现三段式结构:
-
先查缓存:调用
findLoadedClass(name)检查当前类加载器是否已加载过该类;已加载则直接返回,避免重复解析 -
再向上委托:若未加载,且存在父加载器(
parent != null),则递归调用父类的loadClass;若父为null,则尝试findBootstrapClassOrNull(name)—— 这就是 Bootstrap 加载器的桥接点 -
最后自己兜底:父级全部失败后,才调用
findClass(name),这个抽象方法必须由子类重写(如URLClassLoader读文件、自定义加载器从网络拉字节码)
盯住 parent 字段,理解“双亲委派”的物理载体
private final ClassLoader parent 是委派机制的唯一依赖字段,不是继承关系,而是构造时显式传入的引用:
- 所有系统类加载器(
AppClassLoader、ExtClassLoader)都在sun.misc.Launcher初始化时按顺序创建,并严格设置父子引用 - 自定义类加载器若不显式指定
parent,默认使用getSystemClassLoader()(即 AppClassLoader)作为父加载器 - 注意:
Bootstrap ClassLoader是 C++ 实现,Java 层不可见,所以它的“父”在代码中表现为null,而非一个对象
关注 findClass → defineClass 链路,看清“加载”如何落地为 Class 对象
findClass 只负责获取字节码(byte[]),真正把二进制数据变成 JVM 可识别的 Class 实例,靠的是 defineClass:
-
defineClass是ClassLoader的受保护本地方法(native),它完成字节码校验、生成运行时数据结构、在方法区注册类信息等底层动作 - 子类重写
findClass后,必须调用defineClass(不能跳过),否则字节码不会生效;这也是自定义加载器最常出错的地方 - 例如
URLClassLoader.findClass内部会调用findResource定位 .class 文件,读取后交由defineClass处理
结合 JVM 启动流程,定位类加载器初始化时机
不脱离上下文看源码,才能理解为什么某些字段或行为如此设计:
- JVM 启动时,
java.c中的JavaMain()会先调用InitializeJVM,触发 Bootstrap 加载器加载rt.jar等核心类 - 随后
Launcher类静态初始化,创建ExtClassLoader和AppClassLoader,并把前者设为后者的parent - 此时
System.getProperty("java.class.path")已就绪,AppClassLoader才能据此构建类路径 —— 这解释了为什么findClass在不同加载器中查找策略不同
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











