为什么JVM使用双亲委派?

1.为什么要有双亲委派?

首先明确一点:jvm如何认定两个对象同属于一个类型,必须同时满足下面两个条件:

  • 都是用同名的类完成实例化的。
  • 两个实例各自对应的同名的类的加载器必须是同一个。比如两个相同名字的类,一个是用系统加载器加载的,一个扩展类加载器加载的,两个类生成的对象将被jvm认定为不同类型的对象。

所以,为了系统类的安全,类似java.lang.Object这种核心类,jvm需要保证他们生成的对象都会被认定为同一种类型
即:通过代理模式,对于 Java 核心库的类的加载工作由启动类加载器(bootstrap)来统一完成,保证了 Java 应用所使用的都是同一个版本的 Java 核心库的类,是互相兼容的

说白了就是,遇到一个类,都交给上层处理,这样在不同的地方加载同一个类比如Object时,两个地方都会把Object往上递交给启动类(引导类)加载器,那么生成的对象就是同一个类型(Object类型)的。越往上,覆盖的范围越大。

如果父加载器表示自己无法完成,才会让子类加载器加载。

比如有类加载请求,那么这个请求会被一层层往上

自定义加载器1 -> 应用程序类加载器->扩展类加载器->启动类加载器

一直传到了启动类加载器,

(1)启动类加载器说,我处理不了,儿子(扩展类加载器)你自己处理吧

(2)扩展类加载器一看,自己也处理不了,说 我也处理不了,儿子(应用程序类加载器)你自己处理吧

(3)应用程序类加载器一看,自己也处理不了,说 俺还是没办法处理,儿子(自定义类加载器1)你自己处理吧(一般我们自己写的Java类都在这一层加载)

(4)自定义类加载器1说,你们都不行,我一层一层的往上给你们传,让你们先处理,结果你们都处理不了,那还是我自己来处理把!

2.什么叫类加载器处理不了?

BootStrap启动类加载器为例子,

该加载器由C++实现,加载的是<JAVA_HOME>/lib下的class文件,或-Xbootclasspath参数指定的路径下的jar包加载到内存中,注意必由于虚拟机是按照文件名识别加载jar包的,如rt.jar,如果文件名不被虚拟机识别,即使把jar包丢到lib目录下也是没有作用的(出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类)。所以,字节码class文件如果不是这种形式,比如我们瞎写的类,它就不可能被bootstrap加载。

扩展类加载器,由Java语言实现的,它负责加载<JAVA_HOME>/lib/ext目录下或者由系统变量-Djava.ext.dir指定位路径中的类库,开发者可以直接使用标准扩展类加载器。

系统类加载器也称应用程类加载器 它负责加载系统类路径java -classpath-D java.class.path指定路径下的类库,也就是我们经常用到的classpath路径,开发者可以直接使用系统类加载器,一般情况下该类加载是程序中默认的类加载器,通过ClassLoade.getSystemClassLoader()方法可以获取到该类加载器。

系统自带三个类加载器都加载特定目录下的类,如果我们自己的类加载器加载一个特殊的目录,那么系统的加载器就无法加载,也就是最终还是由我们自己的加载器加载。

3.能不能自己写个类叫java.lang.System?

答案:通常不可以,但可以采取另类方法达到这个需求。

解释:为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,爸爸们能找到的类,儿子就没有机会加载。而System类是Bootstrap加载器加载的,就算自己重写,也总是使用Java系统提供的System,自己写的System类根本没有机会得到加载。

但是,我们可以自己定义一个类加载器来达到这个目的,为了避免双亲委托机制,这个类加载器也必须是特殊的。由于系统自带的三个类加载器都加载特定目录下的类,如果我们自己的类加载器加载一个特殊的目录,那么系统的加载器就无法加载,也就是最终还是由我们自己的加载器加载

猜你喜欢

转载自blog.csdn.net/qq_43778308/article/details/110732582