跳转到主内容
极星编程网:以代码为星,赴技术山海!

如何通过类加载器层次结构可视化掌握复杂变量加载路径

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

相关文章