专栏首页Java进阶指南带你搞懂双亲委派机制

带你搞懂双亲委派机制

前言

之前详细介绍了Java类的整个加载过程(类加载机制你真的了解吗?)。虽然,篇幅较长,但是也不要被内容吓到了,其实每个阶段都可以用一句话来概括。

1)加载:查找并加载类的二进制字节流数据。

2)验证:保证被加载的类的正确性。

3)准备:为类的静态变量分配内存,并设置默认初始值。

4)解析:把类中的符号引用转换为直接引用。

5)初始化:为类的静态变量赋予正确的初始值。

当然,要想掌握类加载机制,还是需要去深入研究的。(好吧,说了一句正确的废话)因为其中,有很多知识点也是面试中常问的。比如,我之前去面试的时候,面试官就问到了一个和类初始化相关的问题。就是给一段代码,有父子类关系,父子类中包含静态代码块、构造代码块、普通代码块、构造函数等,然后让判断代码最终的执行顺序。(可自行思考一下,具体内容细节暂时不做扩展)

类加载器

终于来到了本文的主题 —— 类加载器和双亲委派机制。

在《深入理解Java虚拟机》中,对于类加载器的定义是这样的:

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

简单来说,类加载器的作用就是去加载class类的二进制字节流的。

类加载器有以下三种:

1)启动类加载器(Bootstrap ClassLoader),或者叫根加载器。这个类加载器主要是去加载你在本机配置的环境变量 Java_Home/jre/lib 目录下的核心API,如rt.jar

2)扩展类加载器(Extension ClassLoader)。这个加载器负责加载 Java_Home/jre/lib/ext 目录下的所有jar包。

3)应用程序类加载器(Application ClassLoader)。这个加载器加载的是你的项目工程的ClassPath目录下的类库。如果用户没有自定义自己的类加载器,这个就是程序默认的类加载器。

另外,如果有需要的话,用户也可以自定义自己的类加载器(去继承ClassLoader类)。

我们也可以通过代码把类加载器打印出来:

public class TestClassLoader {
    public static void main(String[] args) {
        Object obj = new Object();
        System.out.println(obj.getClass().getClassLoader());

        TestClassLoader t = new TestClassLoader();
        System.out.println(t.getClass().getClassLoader());
        System.out.println(t.getClass().getClassLoader().getParent());
        System.out.println(t.getClass().getClassLoader().getParent().getParent());
    }
}

打印结果:

null
sun.misc.Launcher$AppClassLoader@58644d46
sun.misc.Launcher$ExtClassLoader@6d6f6e28
null

注意,上面第一行和第四行的null此处可不是空的意思,它代表的是启动类加载器。因为启动类加载器是用C++代码来实现的,严格来说不属于Java类,所以Java代码访问不到,故返回null。第二行是应用程序类加载器,第三行是扩展类加载器。

双亲委派机制

在介绍双亲委派机制之前,先观察一下以下代码能否正确运行:

//自己定义的一个 java.lang包
package java.lang;

public class String {
    public static void main(String[] args) {
        String s = new String();
        System.out.println(s);
    }
}

以上代码,编译没有任何问题,但是运行时,却报错:

为什么提示在java.lang.String类中找不到main方法呢,我这明明不是定义了吗?其实,问题的关键就在于类加载遵循双亲委派机制。

类加载器有以下这样的层次关系:

当一个类在加载的时候,都会先委派它的父加载器去加载,这样一层层的向上委派,直到最顶层的启动类加载器。如果顶层无法加载(即找不到对应的类),就会一层层的向下查找,直到找到为止。这就是类的双亲委派机制。

这样做有什么好处呢?这就相当于维护了一个有优先级的层级关系,即总是从最顶层的父加载器开始加载。这就如同,你工作中遇到了问题需要向上反馈,比如先反馈给小组长,然后小组长反馈给上级经理,最后经理反馈给boss。然后boss感觉这问题太简单了不需要他亲自出手,让经理自己解决吧,然后经理又向下交给小组长。小组长一看,这问题不算难,人也比较热心,于是就帮你把问题解决了。(可能例子不是太恰当哈,意思理解即可)

到此,我们就明白了为什么上边的代码会报错。因为双亲委派机制的存在,去加载我们自己定义的“java.lang.String”类的时候,会最终委派到顶层的启动类加载器,然后找到了rt.jar包下的“java.lang.String”。找到之后,就直接加载rt.jar包的String类(也就是我们经常使用的那个字符串类),不再去向下查找,也就加载不了我们自定义的String类了。由于,rt.jar包下的String类中确实没有main方法,所以才会有以上的报错信息。

我们可以试想一下,如果没有双亲委派机制的存在,那我这段代码是不是就可以执行成功了。如果这样的话,岂不是说明我可以随意覆盖rt.jar包中的类(如String,Integer类等)。这样的话将会使程序陷入混乱,Java核心包中的类的安全也无法保证。

本文分享自微信公众号 - 烟雨星空(mistyskys),作者:烟雨星空

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-02-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 双亲委派机制

    孙晨c
  • JVM - 双亲委派机制

    父加载器不是“类加载器的父类加载器”!!! 双亲委派是一个孩子向父亲(上级)方向,然后父亲向孩子方向的双亲委派过程

    Parker
  • 1.4 打破双亲委派机制

    比如, 我现在有一个自定义类加载器, 加载的是~/com/lxl/jvm/User1.class类, 而在应用程序的target目录下也有一个com/lxl/...

    用户7798898
  • 为何采用双亲委派机制

    java中存在3种类型的类加载器:引导类加载器,扩展类加载器和系统类加载器。三者是的关系是:引导类加载器是扩展类加载器的父类,扩展类加载器是系统类加载器的父类。

    allsmallpig
  • JVM笔记二双亲委派机制

    JVM类加载器是什么机制?为什么使用这种机制(这种机制的好处是什么)?说下类加载流程?用代码验证类加载机制。为什么要破坏类的这种加载机制?

    凯哥Java
  • 详细讲解!从JVM直到类加载器

    整个过程是,x.java文件需要编译成x.class文件,通过类加载器加载到内存中,然后通过解释器或者即时编译器进行解释和编译,最后交给执行引擎执行,执行引擎操...

    java技术爱好者
  • 自定义ClassLoader和双亲委派机制

    博文主要讲classloader的模型、作用和使用,内容是作者学习java反射机制有关知识时记录的笔记。

    静默加载
  • Android类加载之双亲委派机制

    我们定义一个CustomClassLoader加载器,去加载String.class文件的流程如下:

    码农帮派
  • 1.2 双亲委派机制及其原理

    参考博客: https://www.cnblogs.com/ITPower/p/13197220.html

    用户7798898
  • [五]类加载机制双亲委派机制 底层代码实现原理 源码分析 java类加载双亲委派机制是如何实现的

    本文是双亲委派机制的源码分析部分,类加载机制中的双亲委派模型对于jvm的稳定运行是非常重要的

    noteless
  • JVM - 彻底理解打破双亲委派机制

    我们希望通过自定义加载器 直接从某个路径下读取Artisan.class . 而不是说 通过自定义加载器 委托给 AppClassLoader ------> ...

    小小工匠
  • 3分钟了解Java双亲委派机制

    在介绍双亲委派机制的时候,不得不提ClassLoader。说ClassLoader之前,我们得先了解下Java的基本知识。 Java是运行在Java的...

    吴延宝
  • java架构之路-(九)JVM类的加载机制

    ***.class文件执行大概就是这样来走的。我们都知道我们的java文件经过编译以后会生成对应的class文件。先经过类装载子系统,然后塞进运行时内存模型的...

    小菜的不能再菜
  • 谈谈类加载器的双亲委派机制

    负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类

    葆宁
  • Java 类加载机制及双亲委派模型

    即 加载 → \rightar...

    Steve Wang
  • JVM类加载机制和双亲委派模型

    虚拟机类加载机制:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。

    用户3467126
  • JVM系列——java文件到JVM中的整个过程

    首先是编写一个HelloWorld.java类,然后通过这一系列的编译操作,最终成了HelloWorld.class文件。然后把HelloWorld.class...

    田维常
  • Tomcat 类加载器打破双亲委派模型

    1. 什么是类加载机制? 2. 什么是双亲委任模型? 3. 如何破坏双亲委任模型? 4. Tomcat 的类加载器是怎么设计的?

    爱撸猫的杰
  • Java虚拟机类加载器及双亲委派机制

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    程序新视界

扫码关注云+社区

领取腾讯云代金券