虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到虚拟机外部去实现,以便让程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”。
类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用远远不限于类加载阶段。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每个类加载器都有一个独立的类名称空间。比较两个类是否“相等”,只有在这两个类由同一个类加载器的前提下才有意义,否则,即使两个类源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类必然不等。这里的相等,包括代表Class对象的equals()、isAssignableFrom()、isInstance()方法。
从Java虚拟机的角度讲,只存在两种不同的加载器:一种是启动类加载器,使用C++实现,是虚拟机自身的一部分;另一种就是其他所有类加载器,由Java实现,独立于虚拟机外部,并且都继承自抽象类 java.lang.ClassLoader。
从Java开发人员的角度讲,绝大部分Java程序会使用一下三种系统提供的类加载器。
(1)启动类加载器:负责将<JAVA_HOME>\lib目录中的或者被 -Xbootclasspath参数所指定的路径中的,并且被虚拟机识别(文件名识别)类库加载到虚拟机内存中。
(2)拓展类加载器:负责加载<JAVA_HOME>\lib\ext 目录中,或者被 java.ext.dirs 系统变量所指定的路径中所有库类,开发者可以直接使用拓展类加载器。
(3)应用程序类加载器:负责加载用户路径上所指定的类库,开发者可以直接使用这个类加载器,也是程序中默认的类加载器。
我们的应用程序都是由这3种类加载器互相配合进行加载的,如果有必要,也可以定义自己定义的类加载器。由(1)到(3)的这种层次关系被称为双亲委派模型(Parents Delegation Model)。
双亲委派模型:除了顶层的启动类加载器,其余的加载器都应当有自己的父类加载器,这里使用组合关系来复用父加载器的代码。如果一个类加载器收到了类加载请求,它首先不会去尝试加载这个类,而是把这个请求委派给父类加载器去完成。只有当父类加载器无法完成这个加载请求时,子加载器才会尝试自己去加载。如此就保证了Java类随着类加载器一起具备了一种带有优先级的层次模型。