掌握类加载器层次结构的关键在于理解“请求—委托—落点”链路:Bootstrap加载核心类(如String)且getClassLoader()返回null;ExtClassLoader加载jre/lib/ext下类;AppClassLoader加载classpath类;自定义加载器通过重写loadClass可观察双亲委派全过程。
掌握类加载器层次结构的关键,不在于死记硬背三类系统加载器的名称,而在于看清字节码从哪来、被谁加载、为何走特定路径——可视化本质是还原“请求—委托—落点”的链路逻辑。
看懂三类系统加载器的实际加载范围
启动类加载器(Bootstrap)用C++实现,不继承
ClassLoader
,因此
返回
。它加载的是
指定路径下的核心类,比如
、
等。可通过以下代码快速验证:
查看其实际扫描路径
返回
,印证其非Java对象
识别扩展与应用加载器的真实分工
扩展类加载器(ExtClassLoader)和应用程序类加载器(AppClassLoader)都是
的子类,由Java编写,且存在明确的父子委托关系:
ExtClassLoader 加载
目录(如
)中的JAR
AppClassLoader 加载
(含
或
环境变量)下的类
执行
,99%情况下返回的就是AppClassLoader实例
用自定义加载器暴露委托链真实走向
写一个简单自定义加载器并重写
,在关键节点打印调用栈,能直观看到双亲委派如何逐级向上:
重写
,开头加
显式调用
前先打印父加载器信息
加载一个
时,会依次输出App → Ext → Bootstrap的尝试过程,最后Bootstrap成功返回
加载一个自定义类
时,前三层都返回
,最终由自定义加载器自己调用
完成
变量加载路径取决于其所属类的加载器
所谓“复杂变量”的加载路径,其实由该变量所在类的加载器决定,而非变量本身:
静态变量
:若所在类由AppClassLoader加载,则该常量值随类一同进入JVM方法区
引用类型变量
:其初始化代码在
中,执行时机由该类是否被主动使用触发,与加载器无关,但加载器决定了
能否被正确解析
数组变量
:数组类由JVM直接生成,其元素类型
由Bootstrap加载,因此数组类的
也返回
getClassLoader()nullsun.boot.class.pathjava.lang.Stringjava.util.ArrayListSystem.getProperty("sun.boot.class.path")String.class.getClassLoader()nullClassLoaderjava.ext.dirsjre/lib/extclasspath-cpCLASSPATHMyClass.class.getClassLoader()loadClassloadClass(String name, boolean resolve)System.out.println("【" + this + "】尝试加载: " + name)super.loadClass(name, resolve)java.lang.Objectcom.example.FoonulldefineClasspublic static final String CONFIG = "prod"private static DataSource ds = new HikariDataSource()HikariDataSourceString[] arr = new String[10]StringgetClassLoader()null