之前说到类加载机制的相关原理,这次来谈谈类加载器以及双亲委派模型的概念。
类加载器
类加载器是Java进行类加载的重要部分,但它的作用又不仅仅局限于进行类加载这么简单。对于两个类,如果要比较它们是否“相等”,首先要确认这两个类来自通过一个类加载器,否则谈论这两个类是否相等将毫无意义。 这里的“相等”,包括代表类的Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回结果。
类加载器一般被分为三种:启动类加载器、扩展类加载器、应用程序类加载器。
启动类加载器
启动类加载器与其他类加载器不同,它是由C++实现的类加载器,是在虚拟机内部的一部分,而其他加载器都是独立于Java虚拟机存在的。启动类加载器用来加载%JAVA_HOME%lib
目录或者被-Xbootclasspath
参数指定的目录下的类库到虚拟机内存中,且要求类库文件名是Java虚拟机能够识别的,如rt.jar和tools.jar等。
扩展类加载器
扩展类加载器负责加载%JAVA_HOME%libext
目录或者被java.ext.dirs
系统变量所指定的目录下的类库。
应用程序类加载器
应用程序类加载器也被称为系统类加载器,负责加载用户类路径上的所有类库。如果用户没有自定义类加载器,那么默认使用该类加载器进行类加载。
双亲委派模型
双亲委派模型结构如上图所示,除了顶层的启动类加载器外,其他类加载器都有自己的父类加载器。下面的代码演示了类加载器的层次结构。
public class TestClassLoaderParent {
public static void main(String[] args) {
System.out.println("TestClassLoaderParent's classLoader is "
+ TestClassLoaderParent.class.getClassLoader());
System.out.println("The parent of testClassLoaderParent's classLoader is "
+ TestClassLoaderParent.class.getClassLoader().getParent());
System.out.println("The grandparent of testClassLoaderParent's classLoader is "
+ TestClassLoaderParent.class.getClassLoader().getParent().getParent());
}
}
输出结果如下:
TestClassLoaderParent's classLoader is sun.misc.Launcher$AppClassLoader@73d16e93
The parent of testClassLoaderParent's classLoader is sun.misc.Launcher$ExtClassLoader@15db9742
The grandparent of testClassLoaderParent's classLoader is null
最后输出的应用程序类加载器的祖父类加载器显示是null,这并不意味着这个祖父类加载器存在,而是代表启动类加载器。
双亲委派模型的原理是当一个类加载器接收到类加载请求,这个类加载器不会自己尝试加载,而是让自己的父类加载器去完成,直到请求传送到最顶层的启动类加载器。如果父类加载器无法完成加载,则会让子类去加载。双亲委派模型的代码实现非常简单,代码如下所示。
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
双亲委派模型的优点是越高层级的类加载器的优先级越高,这样无论加载任何类都会优先分配给最顶端的启动类加载器进行加载。例如如果我们需要加载java.lang.Object类,那么双亲委派机制可以保证启动类加载器每次从rt.jar中进行加载,而不是由别的类加载器加载,也确保了Java基础体系的安全性。
欢迎关注我的公众号:SKY技术修炼指南