双亲委派介绍
双亲委派机制(Parent Delegation Mechanism)是Java中的一种类加载机制。在Java中,类加载器负责加载类的字节码并创建对应的Class对象。双亲委派机制是指当一个类加载器收到类加载请求时,它会先将该请求委派给它的父类加载器去尝试加载。只有当父类加载器无法加载该类时,子类加载器才会尝试加载。
这种机制的设计目的是为了保证类的加载是有序的,避免重复加载同一个类。Java中的类加载器形成了一个层次结构,根加载器(Bootstrap ClassLoader)位于最顶层,它负责加载Java核心类库。其他加载器如扩展类加载器(Extension ClassLoader)和应用程序类加载器(Application ClassLoader)都有各自的加载范围和职责。通过双亲委派机制,可以确保类在被加载时,先从上层的加载器开始查找,逐级向下,直到找到所需的类或者无法找到为止。
这种机制的好处是可以避免类的重复加载,提高了类加载的效率和安全性。同时,它也为Java提供了一种扩展机制,允许开发人员自定义类加载器,实现特定的加载策略。
类加载器介绍
上面说到了几个类加载器,小伙伴们可能有点懵,我简单介绍下3个最重要的类加载器。
- Bootstrap classLoader:这个加载器是最顶层的加载器,主要加载核心类库,
%JRE_HOME%\lib
下的jar包,在jdk源码中其实是找不到这个类的,它是使用C/C++语言实现的,本身是虚拟机的一部分。不过能够在ClassLoader类中调用findBootstrapClass(String name)
方法取得这个类,不过该方法也是native方法,看不到具体实现。 - ExtClassLoader:这个是扩展的类加载器,加载目录
%JRE_HOME%\lib\ext
目录下的jar包。需要注意的是Bootstrap classLoader并不是ExtClassLoader严格意义上的父加载器,ExtClassLoader实例中有个变量是ClassLoader parent
,这个变量的值是null。当ClassLoader执行loadClass(String name, boolean resolve)
方法时,检查到parent
值是null,然后会执行findBootstrapClassOrNull(String name)
找到Bootstrap classLoader。 - AppClassLoader:这个类加载当前应用的classpath的所有类。它的
ClassLoader parent
是ExtClassLoader。AppClassLoader和ExtClassLoader最顶层的父类是ClassLoader,ClassLoader是个抽象类。
类加载流程
类的加载流程用一句话简单说就是向上委托,向下查找加载。流程如下:
测试Demo
我在我自己的项目中创建了 一个java.lang
的包 ,然后创建了一个 String 类。
再准备一个测试类,使用这个String类创建对象。
运行结果:
并没有输出我的String 里面的static 静态代码块和构造函数中的内容。
并没有输出我的String 里面的static 静态代码块,证明使用的仍然是 jdk 自带的。
原因是什么呢?
我们自定义一个类,你要想加载的话,应该是用 Application ClassLoader(系统类加载器、应用程序加载器)进行加载。但是这个时候又牵扯到了 双亲委派机制。
1、当我们要加载这个自定义String时,
2、先是让应用程序加载器(Application ClassLoader)加载,但是发现它上面还有扩展类加载器(Extension ClassLoader)
3、接着委托给扩展类加载器(Extension ClassLoader),突然发现它上面还有Bootstrap ClassLoader (启动类加载器)
4、就又接着委托到了Bootstrap ClassLoader (启动类加载器)。启动加载器一看,这是 java.lang 包下的,于是把它加载,然后成功返回。所以这里使用的 new String() 实际使用的还是 java 中 String。
这样子可以防止什么样问题的发生呢?
-
避免类的重复加载:当一个类需要被加载时,JVM首先会委派给父类加载器进行加载,只有当父类加载器无法加载该类时,才会由当前类加载器进行加载。这样可以有效避免类的重复加载,节省了内存空间。
-
确保类的安全性:通过双亲委派机制,JVM可以对类加载的层次结构进行管理和控制,从而确保类的安全性。只有由受信任的类加载器加载的类才能被使用,防止恶意类的加载和执行。
-
提高代码的稳定性和可靠性:通过双亲委派机制,JVM可以保证核心类库的一致性和稳定性。当核心类库中的类被加载时,始终使用同一个类加载器进行加载,避免了不同类加载器加载同一个类的情况,从而保证了代码的稳定性和可靠性。
-
支持类加载的定制化:通过自定义类加载器可以实现对类加载的定制化,根据不同的需求加载不同的类。双亲委派机制使得自定义类加载器可以在委派链的某个节点进行加载,从而实现对加载过程的控制和定制。
总结
最后总结一下,这种设计有个好处是,如果有人想替换系统级别的类:String.java。篡改它的实现,在这种机制下这些系统的类已经被Bootstrap classLoader加载过了(为什么?因为当一个类需要加载的时候,最先去尝试加载的就是BootstrapClassLoader),所以其他类加载器并没有机会再去加载,从一定程度上防止了危险代码的植入。