类加载器和双亲委派模型

什么是类加载器?

类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作在Java虚拟机外部实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”。

类与类加载器之间的关系

对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。也就是说:比较两个类是否“相等”,只有在这个两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那么这两个类就必定不想等。

package cn.hao.test;

import java.io.IOException;
import java.io.InputStream;

public class ClassLoaderTest {

    public static void main(String[] args) throws Exception {
        ClassLoader myLoader = new ClassLoader() {
            @Override
            public Class<?> loadClass(String name)
                    throws ClassNotFoundException {
                try {
                    String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                    InputStream is = getClass().getResourceAsStream(fileName);
                    if (is == null) {
                        return super.loadClass(name);
                    }
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name, b, 0, b.length);
                } catch (IOException e) {
                    throw new ClassNotFoundException(name);
                }
            }
        };
        Object obj = myLoader.loadClass("cn.hao.test.ClassLoaderTest").newInstance();
        System.out.println(obj.getClass());
        System.out.println(obj instanceof cn.hao.test.ClassLoaderTest);
    }
}

运行结果:

class cn.hao.test.ClassLoaderTest
false

使用自定义加载器加载了一个名为“cn.hao.test.ClassLoaderTest”的类,并且实例化了这个类的一个对象。输出结果表明,这个对象确实是类cn.hao.test.ClassLoaderTest实例化出的一个对象,但是这个对象与类cn.hao.test.ClassLoaderTest做所属类型检查的时候返回了false,因为虚拟机中存在了两个ClassLoaderTest类,一个由系统应用程序类加载器加载的,另一个是由自定义的类加载器加载的,虽然都来自同一个Class文件,但是它们依然是两个独立的类,因此做对象所属类型检查的结果是false。

Java虚拟机中类加载器

从Java虚拟机的角度来看,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),是虚拟机自身的一个部分;另一种是所有其他类加载器,这些类加载器独立于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader。

从Java开发人员的角度看,绝大多数Java程序都会使用到以下3种系统提供的类加载器:

启动类加载器(Bootstrap ClassLoader):负责将存在<JAVA_HOME>\lib目录中,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(如rt.jar)类库加载到虚拟机内存中。

扩展类加载器(Extension ClassLoader):复负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。

应用程序类加载器(Application ClassLoader):一般也被称为系统类加载器。负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

双亲委派模型

下图显示的类加载器之间的这种层次关系,称为类加载器的双亲委派模型(Parents Delegation Model)。

双亲委派模型的工作过程:

如果一个类加载器收到了类加载的契求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传递到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

使用双亲委派模型的好处:

最大和好处就是保证Java中最基础的行为不会被破坏。例如类java.lang.Object,它存在rt.jar之中,无论哪个类加载器要加载这个类,最终都会委派给处于模型最顶层的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一类。相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个名称为java.lang.Object的类,并放在CLASSPATH中,那系统中会将出现多个不同的Object类,Java类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱。

参考

1、周志明,深入理解Java虚拟机:JVM高级特性与最佳实践,机械工业出版社 

原文地址:https://www.cnblogs.com/binaway/p/8795116.html