双亲委托模型(Parent Delegation Model)是Java类加载器(ClassLoader)采用的一种特定的工作方式。在这个模型中,类加载器在尝试加载某个类时,首先会委托给它的父加载器去尝试加载这个类,这个过程会一直递归进行,直到顶层的启动类加载器(Bootstrap ClassLoader)。如果父加载器能够成功加载该类,则返回;如果父加载器无法完成这个任务,那么子加载器才尝试自己去加载这个类。
双亲委托模型的好处
-
避免类的重复加载: 由于顶层开始尝试加载,所以同一个类不会被加载多次。
-
安全性考虑: 防止核心API被随意篡改。例如,你无法通过自定义一个名为
java.lang.Object
的类来代替核心库中定义的java.lang.Object
类。
类加载器的层级结构
在JVM中,类加载器通常具备以下的层级结构:
- 启动类加载器(Bootstrap ClassLoader): 这个类加载器加载Java的核心库,是用原生代码实现的,并不继承自
java.lang.ClassLoader
。 - 扩展类加载器(Extension ClassLoader): 这个加载器加载Java的扩展库,Java平台的扩展功能实现类通常位于
jre/lib/ext
目录下或者由java.ext.dirs
系统属性指定的路径中。 - 系统(应用)类加载器(System or Application ClassLoader): 这个加载器负责加载用户路径(即classpath指定的所有路径)上的类库,如果没有特别指定,则用户自定义的类加载器通常其这个加载器为父加载器。
示例
让我们来看一下实际代码中类加载器如何工作。假设我们要加载叫做MyClass
的类:
public class MyClass {
static {
System.out.println("MyClass initialized!");
}
}
接下来启动Java程序,使用自定义的类加载器加载上面的类:
public class CustomClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// 明确拒绝加载MyClass类,其余的类都委派给父加载器来加载
if ("MyClass".equals(name)) {
throw new ClassNotFoundException("Not allowed to load MyClass");
}
return super.loadClass(name);
}
public static void main(String[] args) throws Exception {
CustomClassLoader loader = new CustomClassLoader();
Class<?> clazz = loader.loadClass("MyClass");
}
}
如果按照双亲委托模型,CustomClassLoader
会委派其父加载器来加载MyClass
,父加载器会向上委派至最顶层,从而保证了类的统一加载。
这是一个简化的例子来解释双亲委托模型,实际的加载细节要比这复杂的多,像findClass
方法的使用,加载过程中的安全性检查等都是需要处理的。
双亲委托模型在Java类加载中提供了一定的规则,确保了Java平台的稳定性和安全性。不过,也有一些情况会打破这个模型,例如OSGi就提供了一种不同于双亲委托的类加载方式。在某些复杂应用中,可能会根据需要自定义类加载器,到时候可能需要适当地绕过双亲委托模型。