一、类加载器:JVM 的 "快递分拣员"
类加载器是 Java 虚拟机(JVM)的 "快递分拣员",负责将磁盘上的.class
文件加载到内存中,让 JVM 能识别和使用这些类。想象一个工厂:
- Bootstrap ClassLoader:顶层分拣员(C++ 实现),只负责核心快递(如 Java 基础类
rt.jar
中的java.lang.Object
)。 - Extension ClassLoader:中层分拣员(Java 实现),处理扩展快递(如
JAVA_HOME/lib/ext
目录下的类库)。 - Application ClassLoader:底层分拣员(Java 实现),处理用户快递(应用代码和依赖的 jar 包)。
二、双亲委派模型:类加载的 "等级制度"
类加载器遵循 "双亲委派" 规则,就像员工遇到问题先问上级:
-
当一个类加载器收到加载请求时,先交给父加载器处理;
-
父加载器依次向上传递,直到顶层的 Bootstrap ClassLoader;
-
若父加载器无法加载,才由当前加载器尝试加载。
优势:
-
安全保障:防止用户自定义类覆盖核心类。例如,即使自己写了
java.lang.Object
,Bootstrap 加载器会优先加载官方Object
,避免混乱。 -
避免重复加载:同一个类只需加载一次,节省内存。
核心代码逻辑:
java
protected Class<?> loadClass(String name, boolean resolve) {
// 先检查是否已加载
Class c = findLoadedClass(name);
if (c == null) {
// 交给父加载器处理
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
// 父加载器无法加载时,自己尝试加载
if (c == null) {
c = findClass(name); // 自定义加载器需重写此方法
}
}
return c;
}
三、自定义类加载器:打造专属 "快递员"
为什么需要自定义?比如实现热更新、插件化。每个类加载器有独立的 "命名空间",类的唯一性由加载器 + 类本身决定。
案例:自定义加载器加载同一类
java
// 自定义加载器
ClassLoader customLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
// 从指定路径加载class文件
String className = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream is = getClass().getResourceAsStream(className);
byte[] data = is.readAllBytes();
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
return super.loadClass(name);
}
}
};
// 加载同一类
Class<?> clazz1 = customLoader.loadClass("com.demo.Test");
Class<?> clazz2 = Class.forName("com.demo.Test"); // 使用系统加载器
// 输出结果:false
System.out.println(clazz1.newInstance() instanceof clazz2);
关键结论:即使类名相同,不同加载器加载的类视为不同类,因为它们的 "快递员" 不同。
四、经典应用场景:现实中的类加载器妙用
-
Tomcat 服务器
- 隔离性:每个 Web 应用有独立的类加载器,避免不同应用的类库冲突(如两个应用使用不同版本的 Spring)。
- 共享性:公共类库(如 servlet-api)由上层加载器共享,减少内存占用。
-
OSGi(模块化系统)
- 用于 Eclipse 等插件系统,支持动态加载、卸载模块,每个插件有独立加载器,实现 "热插拔"。
-
热修复与插件化
- 如微信的热修复框架,通过自定义加载器动态加载修复后的类,无需重启应用。
五、总结:类加载器的核心价值
-
分层协作:Bootstrap、Extension、Application 三级加载器分工明确,确保核心类安全,用户类灵活加载。
-
双亲委派:先问父加载器,避免重复和恶意类覆盖,保障 JVM 稳定。
-
自定义扩展:通过重写
findClass
方法,实现动态加载、隔离等高级功能,是插件化、热修复的基础。
理解类加载器,就像掌握了 JVM 的 "物流系统",无论是排查类冲突问题,还是开发插件化框架,都是必不可少的核心知识。