类加载机制--双亲委派机制

类加载机制–双亲委派机制


说双亲委派机制之前,先来谈谈类加载器

类加载器

在这里插入图片描述
看上述图之后,就知道类加载器有四类

1、启动类加载器(Bootstrap ClassLoader)

这个类将器负责将存放在<JAVA_HOME>\lib目录中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给引导类加载器,那直接使用null指定父类加载器

2、扩展类加载器(Extension ClassLoader)

这个加载器由sun.misc.Launcher $ExtClassLoader实现,它负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器

3、应用类加载器(Application ClassLoader)

这个类加载器由sun.misc.Launcher $App-ClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

4、自定义加载器

自己定义的类加载器,用于自己加载自己所定义的类,一般不写参数,默认为父类为应用类加载器,如果参数为null,则父类为启动类加载器。一般继承ClassLoader然后重写构造方法。
在这里插入图片描述

双亲委派机制

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

通俗来说:就是想要加载一个类,都会交给自己父类进行处理,如果父类处理不了,子类才会进行加载,因为每个父类都有自己的加载范围。

双亲委派模型对于保证Java程序的稳定运作很重要,但它的实现却非常简单,实现双亲委派的代码都集中在java.lang.ClassLoader的loadClass () 方法之中
逻辑清晰易懂:先检查是否已经被加载过,若没有加载则调用父加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父类加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。

看下实现源码

private final ClassLoader parent;

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 首先检查是否已经加载了该类
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                // 父类加载器加载,如果父类加载器为空,使用最顶级的Bootstrap类加载器来加载
                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.
                // 如果以上步骤还没找到类,就按照自己定义的类加载器中重写的findClass方法加载
                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;
    }
}

注意:双亲委派的具体逻辑就实现在这个方法之中,JDK1.2之后已不提倡用户再去覆盖loadClass()方法,而应当把自己的类加载逻辑写到findClass()方法中,在loadClass()方法的逻辑里如果父类加载失败,则会调用自己的findClass()方法来完成加载,这样就可以保证新写出来的类加载器是符合双亲委派规则的。

重写loadClass():不遵循双亲委派机制,按照自己的方式加载
重写findClass():遵循双亲委派机制,只有父类找不到时,才按照自己的方式加载

如果自定义的类加载器遵循双亲委派机制,写了一个Java.lang.String,想去加载自己定义的这个String类,然后就会抛出异常,就会说不能用java.lang包名,如果非要加载自己定义的(和java内库中同名的类),就要手动重写loadClass(),就不会遵循双亲委派机制,按照自己的方式加载。

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读