当前位置: 首页 > news >正文

面试官:说说双亲委派模型?

在说双亲委派模型之前首先得先了解一下类加载阶段。

类的加载阶段

类加载阶段分为加载、连接、初始化三个阶段,而加载阶段需要通过类的全限定名来获取定义了此类的二进制字节流。Java特意把这一步抽出来用类加载器来实现。把这一步骤抽离出来使得应用程序可以按需自定义类加载器。并且得益于类加载器,OSGI、热部署等领域才得以在JAVA中得到应用。

在Java中任意一个类都是由这个类本身和加载这个类的类加载器来确定这个类在JVM中的唯一性。也就是你用你A类加载器加载的com.aa.ClassA和你A类加载器加载的com.aa.ClassA它们是不同的,也就是用instanceof这种对比都是不同的。所以即使都来自于同一个class文件但是由不同类加载器加载的那就是两个独立的类。

类加载器除了能用来加载类,还能用来作为类的层次划分。Java自身提供了3种类加载器

1、启动类加载器(Bootstrap ClassLoader),它是属于虚拟机自身的一部分,用C++实现的,主要负责加载<JAVA_HOME>\lib目录中或被-Xbootclasspath指定的路径中的并且文件名是被虚拟机识别的文件。它等于是所有类加载器的爸爸。

2、扩展类加载器(Extension ClassLoader),它是Java实现的,独立于虚拟机,主要负责加载<JAVA_HOME>\lib\ext目录中或被java.ext.dirs系统变量所指定的路径的类库。

3、应用程序类加载器(Application ClassLoader),它是Java实现的,独立于虚拟机。主要负责加载用户类路径(classPath)上的类库,如果我们没有实现自定义的类加载器那这玩意就是我们程序中的默认加载器。

双亲委派模型

知道上面这几个概念就能来看看双亲委派模型了。

双亲委派的意思是如果一个类加载器需要加载类,那么首先它会把这个类请求委派给父类加载器去完成,每一层都是如此。一直递归到顶层,当父加载器无法完成这个请求时,子类才会尝试去加载。这里的双亲其实就指的是父类,没有mother。父类也不是我们平日所说的那种继承关系,只是调用逻辑是这样。

        {
            // First, check if the class has already been loaded 先判断class是否已经被加载过了
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);  //找他爸爸去加载
                    } else {
                        c = findBootstrapClassOrNull(name);  //没爸爸说明是顶层了就用Bootstrap ClassLoader去加载
                    }
                } 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.
                    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;
        }
复制代码

双亲委派模型不是一种强制性约束,也就是你不这么做也不会报错怎样的,它是一种JAVA设计者推荐使用类加载器的方式。

双亲委派有啥好处呢?它使得类有了层次的划分。就拿java.lang.Object来说,你加载它经过一层层委托最终是由Bootstrap ClassLoader来加载的,也就是最终都是由Bootstrap ClassLoader去找<JAVA_HOME>\lib中rt.jar里面的java.lang.Object加载到JVM中。

这样如果有不法分子自己造了个java.lang.Object,里面嵌了不好的代码,如果我们是按照双亲委派模型来实现的话,最终加载到JVM中的只会是我们rt.jar里面的东西,也就是这些核心的基础类代码得到了保护。因为这个机制使得系统中只会出现一个java.lang.Object。不会乱套了。你想想如果我们JVM里面有两个Object,那岂不是天下大乱了。

因此既然推荐使用这种模型当然是有道理了。

但是人生不如意事十之八九,有些情况不得不违反这个约束,例如JDBC。

你先得知道SPI(Service Provider Interface),这玩意和API不一样,它是面向拓展的,也就是我定义了这个SPI,具体如何实现由扩展者实现。我就是定了个规矩。

JDBC就是如此,在rt.jar里面定义了这个SPI,那mysql有mysql的jdbc实现,oracle有oracle的jdbc实现,反正我java不管你内部如何实现的,反正你们都得统一按我这个来,这样我们java开发者才能容易的调用数据库操作。所以因为这样那就不得不违反这个约束啊,Bootstrap ClassLoader就得委托子类来加载数据库厂商们提供的具体实现。因为它的手只能摸到<JAVA_HOME>\lib中,其他的它无能为力。这就违反了自下而上的委托机制了。

Java就搞了个线程上下文类加载器,通过setContextClassLoader()默认情况就是应用程序类加载器然后Thread.current.currentThread().getContextClassLoader()获得类加载器来加载。


如有错误欢迎指正!

个人公众号:yes的练级攻略

有相关面试进阶(分布式、性能调优、经典书籍pdf)资料等待领取

相关文章:

  • 北京
  • yocto添加层简介
  • 渐变文件夹,一定要收下这两套超级精美的文件夹图标!
  • 博客
  • 学习python的第一天-模拟用户登录接口,输入错误三次锁定
  • 如何测量距离?ABViewer热门技术问答精选合集!
  • vue脚手架的报错
  • 在阿里云IIS服务器上安装证书
  • 重磅!阿里云时空数据库正式免费公测
  • 用C#在去中心化交易所OceanOne上挂单买卖任意ERC20 token
  • 时隔五年,Scrapyd 终于原生支持 basic auth
  • (编译到47%失败)to be deleted
  • (一)插入排序
  • 阿里一道Java并发面试题 (详细分析篇)
  • 内核对象句柄
  • [js高手之路]搞清楚面向对象,必须要理解对象在创建过程中的内存表示
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • Android系统模拟器绘制实现概述
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • markdown编辑器简评
  • Webpack入门之遇到的那些坑,系列示例Demo
  • XML已死 ?
  • 测试如何在敏捷团队中工作?
  • 机器学习 vs. 深度学习
  • 警报:线上事故之CountDownLatch的威力
  • 理清楚Vue的结构
  • 判断客户端类型,Android,iOS,PC
  • 悄悄地说一个bug
  • 驱动程序原理
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 我的zsh配置, 2019最新方案
  • 用Canvas画一棵二叉树
  • Unity3D - 异步加载游戏场景与异步加载游戏资源进度条 ...
  • 交换综合实验一
  • # 数论-逆元
  • #微信小程序(布局、渲染层基础知识)
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • (cljs/run-at (JSVM. :browser) 搭建刚好可用的开发环境!)
  • (day 12)JavaScript学习笔记(数组3)
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (三十五)大数据实战——Superset可视化平台搭建
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (算法)求1到1亿间的质数或素数
  • (转载)Linux 多线程条件变量同步
  • (轉貼) 蒼井そら挑戰筋肉擂台 (Misc)
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .NET 设计一套高性能的弱事件机制
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • 。Net下Windows服务程序开发疑惑
  • @Controller和@RestController的区别?
  • @Not - Empty-Null-Blank
  • @require_PUTNameError: name ‘require_PUT‘ is not defined 解决方法
  • [ CTF ] WriteUp- 2022年第三届“网鼎杯”网络安全大赛(白虎组)