当前位置 : 主页 > 编程语言 > 其它开发 >

浅析Java双亲委派机制及其作用

来源:互联网 收集:自由互联 发布时间:2022-06-07
双亲委派机制:当某个类加载器准备加载一个.class字节码文件时,它首先将这个加载任务委派给上一级类加载器,上一级加载器再委派到更上一级类加载器,递归这个操作直到最顶级的

双亲委派机制:当某个类加载器准备加载一个.class字节码文件时,它首先将这个加载任务委派给上一级类加载器,上一级加载器再委派到更上一级类加载器,递归这个操作直到最顶级的类加载器。

一、类加载器的类别

在介绍Java的双亲委派机制的时候,不得不提ClassLoader(类加载器)

我们编译的Java代码是如何在JVM中运行的?首先源程序(.java文件)被Java编译器编译为.class字节码文件,然后ClassLoader负责将这些class文件加载到JVM中去执行。

如上图所示:JVM提供了三层类加载器

  1. BootStrapClassLoader(引导类加载器)

    引导类加载器:用C++编写,是Java自带的类加载器,用于加载JDK内部的类;主要负责加载核心的类库,构造ExtensionClassLoaderAppClassLoader。Bootstrap类加载器用于加载JDK中$JAVA_HOME/jre/lib下面的那些类,比如rt.jar包里面的类。

    由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到引导类加载器的引用,所以不允许直接通过引用进行操作。

  2. ExtensionClassLoader(扩展类加载器)

    扩展类加载器:主要负责加载 jre/lib/ext 目录下的一些扩展的jar包或-Djava.ext.dirs指定目录下的jar包。主要用于加载JDK扩展包里的类。一般$JAVA_HOME/lib/ext下面的包都是通过这个类加载器加载的

    Java编写,加载扩展库,如classpath中的jrejavax.*或者java.ext.dir 指定位置中的类,开发者可以直接使用标准扩展类加载器。

  3. AppClassLoader(系统类加载器)

    系统类加载器:负责java -classpath或-Djava.class.path所指定目录下的类与jar包

    主要负责加载应用程序的主函数类。用来加载开发人员自己平时写的应用代码的类的,加载存放在classpath路径下的那些应用程序级别的类的。

    Java编写,加载程序所在的目录

  4. CustomClassLoader(用户自定义类加载器)

     java编写,用户自定义的类加载器,可加载指定路径的class文件

二、工作原理
  • 如果一个类加载器收到类加载的请求,并不会自己先加载,而是将加载请求委托给父类的加载器进行加载;
  • 如果父类的加载器还存在其父类加载器,则进一步向上委托,递归这个操作,直至到达最高级的引导类加载器;
  • 如果父类加载器可以完成加载请求,则成功返回,如果父类加载器不能完成加载请求,则子加载器才会尝试自己去加载,这就是双亲委派机制;
  • 父类加载器一层层向下分派任务,如果子类加载器能加载则加载此类,如果直到系统类加载器也无法加载,则抛出异常。

从上图我们就容易理解,当一个Test.class文件要加载时:

  • 不考虑用户自定义的类加载器,首先会在AppClassLoader检查是否加载过,如果已经加载过就无需在加载了;如果没有,就会委派到父加载器,然后调用父加载器的loadClass方法

  • 父类加载器同理也会先检查是否加载过,如果没有再向上委派

    这个类似递归的过程,直到到达BootstrapClassLoader之前,都只是检查是否加载过,并不会选择自己加载

  • 直到BootstrapClassLoader,已经没有父加载器了,这是开始考虑自己是否能加载了,如果自己不能加载,则下沉到子类加载器去加载,直到最底层

  • 如果没有任何类加载器能够加载,则抛出ClassNotFoundException异常

三、双亲委派机制的作用
  • 防止同一个.class文件被重复加载。通过委派机制向上问一问,如果已经加载过,就不用再加载一次,保证数据安全
  • 保证核心.class文件不能被篡改。通过委派机制,即使不小心篡改了核心.class文件,也不会被执行,保证了系统级别的类的安全性
四、代码浅析

举例1:建立一个java.lang.String类,写上static代码块

在另外的程序中加载 String 类,看看加载的 String 类是 JDK 自带的 String 类,还是我们自己编写的 String 类

从输出结果可以看到,程序并没有输出静态代码块的内容,自定义String类并没有被加载,所以加载的是JDK内置的String类

这是因为我们自定义的String类本应被AppClassLoader加载,但AppClassLoader并不会先自己加载,而是把加载请求委托给父类的加载器去执行,到了ExtensionClassLoader发现String类不归自己管,于是委托给父类的加载器(BootStrapClassLoader),引导类加载器发现是java.lang包,所以加载的就是JDK内置的String类

举例2:在自定义的String类中写个main()方法

由于双亲委派机制加载的是JDK内置的String类,而在引导类加载器中的核心类库API里的String类并没有main()方法

举例3:在java.lang包下自定义一个AbcTest类

报错,java.lang包下不允许自定义类

网友评论