读者美团五面:Java历史上有三次破坏双亲委派模型,是哪三次?

沙海 2021年5月21日02:23:01Java评论56字数 2982阅读9分56秒阅读模式
摘要

读者美团五面:Java历史上有三次破坏双亲委派模型,是哪三次? 三太子敖丙

读者美团五面:Java历史上有三次破坏双亲委派模型,是哪三次?

三太子敖丙 文章源自JAVA秀-https://www.javaxiu.com/24474.html

以下文章来源于yes的练级攻略,作者是Yes呀文章源自JAVA秀-https://www.javaxiu.com/24474.html

文章源自JAVA秀-https://www.javaxiu.com/24474.html

读者美团五面:Java历史上有三次破坏双亲委派模型,是哪三次?文章源自JAVA秀-https://www.javaxiu.com/24474.html

yes的练级攻略文章源自JAVA秀-https://www.javaxiu.com/24474.html

用接地气的话来分享一些后端技术或写一些想写的。文章源自JAVA秀-https://www.javaxiu.com/24474.html

文章源自JAVA秀-https://www.javaxiu.com/24474.html

这个面试题来自一位群友的面试题分享,就是我组建的那个面试交流群。文章源自JAVA秀-https://www.javaxiu.com/24474.html

读者美团五面:Java历史上有三次破坏双亲委派模型,是哪三次?文章源自JAVA秀-https://www.javaxiu.com/24474.html

其实不止三次,有四次文章源自JAVA秀-https://www.javaxiu.com/24474.html

今天我们就来盘一盘这个面试题,不过在说双亲委派模型之前,我们得先简单了解下类加载。文章源自JAVA秀-https://www.javaxiu.com/24474.html

类加载

我们平常写的代码是保存在一个 .java文件里面,经过编译会生成.class文件,这个文件存储的就是字节码,如果要用上我们的代码,那就必须把它加载到 JVM 中。文章源自JAVA秀-https://www.javaxiu.com/24474.html

读者美团五面:Java历史上有三次破坏双亲委派模型,是哪三次?文章源自JAVA秀-https://www.javaxiu.com/24474.html

当然,加载到 JVM 生成 class 对象的来源不一定得是.class文件,也可以来自网络等等,反正只要是符合 JVM 规范的都行。文章源自JAVA秀-https://www.javaxiu.com/24474.html

而类加载的步骤主要分为:加载、链接、初始化。文章源自JAVA秀-https://www.javaxiu.com/24474.html

加载

其实就是找到字节流,然后将其加载到 JVM 中,生成类对象。这个阶段就是类加载器派上用场的阶段,等下我们再细说。文章源自JAVA秀-https://www.javaxiu.com/24474.html

链接

这个阶段是要让生成的类对象融入到 JVM 中,分别要经历以下三个步骤:文章源自JAVA秀-https://www.javaxiu.com/24474.html

读者美团五面:Java历史上有三次破坏双亲委派模型,是哪三次?文章源自JAVA秀-https://www.javaxiu.com/24474.html

验证就是检验一下加载的类是否满足 JVM 的约束条件,也就是判断是否合规。文章源自JAVA秀-https://www.javaxiu.com/24474.html

准备就是为加载类的静态变量申请内存空间,并赋予初始值,例如是 int 类型那初始值就是 0。文章源自JAVA秀-https://www.javaxiu.com/24474.html

解析就是将符号引用解析成为实际引用,讲人话就是:例如 Yes 类里面引用了一个 XX 类,那一开始 Yes 类肯定不知道 XX 类在内存里面的地址,所以就先搞个符号引用替代一下,假装知道,等类加载解析的时候再找到 XX 类真正地址,做一个实际引用。文章源自JAVA秀-https://www.javaxiu.com/24474.html

这就是解析要做的事情。还有一点,虽说把解析放到链接阶段里面,但是 JVM 规范并没有要求在链接过程中完成解析。文章源自JAVA秀-https://www.javaxiu.com/24474.html

初始化

这个阶段就是为常量字段赋值,然后执行静态代码块,将一堆要执行的静态代码块方法包装成 clinit 方法执行,这个方法会加锁,由 JVM 来保证 clinit 方法只会被执行一次。文章源自JAVA秀-https://www.javaxiu.com/24474.html

所以可以用一个内部静态类来实现延迟初始化的单例设计模式,同时保证了线程安全。文章源自JAVA秀-https://www.javaxiu.com/24474.html

这个阶段完毕之后,类加载过程就 ok 了,可以投入使用啦,再来画个图汇总一下:文章源自JAVA秀-https://www.javaxiu.com/24474.html

读者美团五面:Java历史上有三次破坏双亲委派模型,是哪三次?文章源自JAVA秀-https://www.javaxiu.com/24474.html

双亲委派模型

加载阶段,需要用到类加载器来将 class 文件里面的内容搞到 JVM 中生成类对象。文章源自JAVA秀-https://www.javaxiu.com/24474.html

那什么是双亲委派模型?

双亲委派模型用一句话讲就是子类加载器先让父类加载器去查找该类来加载,父类又继续请求它的父类直到最顶层,在父类加载器没有找到所请求的类的情况下,子类加载器才会尝试去加载,这样一层一层上去又下来。文章源自JAVA秀-https://www.javaxiu.com/24474.html

读者美团五面:Java历史上有三次破坏双亲委派模型,是哪三次?文章源自JAVA秀-https://www.javaxiu.com/24474.html

每个类加载器都有固定的查找类的路径,在 JDK8 的时候一共有三种类加载器。文章源自JAVA秀-https://www.javaxiu.com/24474.html

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

  2. 扩展类加载器(Extension ClassLoader),它是 Java 实现的,独立于虚拟机,主要负责加载<JAVA_HOME>\lib\ext目录中或被 java.ext.dirs 系统变量所指定的路径的类库。文章源自JAVA秀-https://www.javaxiu.com/24474.html

  3. 应用程序类加载器(Application ClassLoader),它是Java实现的,独立于虚拟机。主要负责加载用户类路径(classPath)上的类库,如果我们没有实现自定义的类加载器那这玩意就是我们程序中的默认加载器。文章源自JAVA秀-https://www.javaxiu.com/24474.html

读者美团五面:Java历史上有三次破坏双亲委派模型,是哪三次?文章源自JAVA秀-https://www.javaxiu.com/24474.html

为什么要提出双亲委派模型?

其实就是为了让基础类得以正确地统一地加载文章源自JAVA秀-https://www.javaxiu.com/24474.html

从上面的图可以看出,如果你也定义了一个 java.lang.Object类,通过双亲委派模式是会把这个请求委托给启动类加载器,它扫描<JAVA_HOME>\lib目录就找到了 jdk 定义的 java.lang.Object 类来加载,所以压根不会加载你写的 java.lang.Object类,这就可以避免一些程序不小心或者有意的覆盖基础类。文章源自JAVA秀-https://www.javaxiu.com/24474.html

至此我们已经清楚了什么是双亲委派,和为什么要双亲委派。接下来我们来看看三次破坏。文章源自JAVA秀-https://www.javaxiu.com/24474.html

第一次破坏

在 jdk 1.2 之前,那时候还没有双亲委派模型,不过已经有了 ClassLoader 这个抽象类,所以已经有人继承这个抽象类,重写 loadClass 方法来实现用户自定义类加载器。文章源自JAVA秀-https://www.javaxiu.com/24474.html

而在 1.2 的时候要引入双亲委派模型,为了向前兼容, loadClass 这个方法还得保留着使之得以重写,新搞了个 findClass 方法让用户去重写,并呼吁大家不要重写 loadClass 只要重写 findClass。文章源自JAVA秀-https://www.javaxiu.com/24474.html

这就是第一次对双亲委派模型的破坏,因为双亲委派的逻辑在 loadClass 上,但是又允许重写 loadClass,重写了之后就可以破坏委派逻辑了。文章源自JAVA秀-https://www.javaxiu.com/24474.html

第二次破坏

第二次破坏指的是 JNDI、JDBC 之类的情况。文章源自JAVA秀-https://www.javaxiu.com/24474.html

首先得知道什么是 SPI(Service Provider Interface),它是面向拓展的,也就是说我定义了个规矩,就是 SPI ,具体如何实现由扩展者实现。文章源自JAVA秀-https://www.javaxiu.com/24474.html

像我们比较熟的 JDBC 就是如此。文章源自JAVA秀-https://www.javaxiu.com/24474.html

MySQL 有 MySQL 的 JDBC 实现,Oracle 有 Oracle 的 JDBC 实现,我 Java 不管你内部如何实现的,反正你们这些数据库厂商都得统一按我这个来,这样我们 Java 开发者才能容易的调用数据库操作,所以在 Java 核心包里面定义了这个 SPI。文章源自JAVA秀-https://www.javaxiu.com/24474.html

而核心包里面的类都是由启动类加载器去加载的,但它的手只能摸到<JAVA_HOME>\lib或Xbootclasspath指定的路径中,其他的它鞭长莫及。文章源自JAVA秀-https://www.javaxiu.com/24474.html

读者美团五面:Java历史上有三次破坏双亲委派模型,是哪三次?文章源自JAVA秀-https://www.javaxiu.com/24474.html

而 JDBC 的实现类在我们用户定义的 classpath 中,只能由应用类加载器去加载,所以启动类加载器只能委托子类来加载数据库厂商们提供的具体实现,这就违反了自下而上的委托机制。文章源自JAVA秀-https://www.javaxiu.com/24474.html

具体解决办法是搞了个线程上下文类加载器,通过setContextClassLoader()默认情况就是应用程序类加载器,然后利用Thread.current.currentThread().getContextClassLoader()获得类加载器来加载。文章源自JAVA秀-https://www.javaxiu.com/24474.html

这就是第二次破坏双亲委派模型。文章源自JAVA秀-https://www.javaxiu.com/24474.html

第三次破坏

这次破坏是为了满足热部署的需求,不停机更新这对企业来说至关重要,毕竟停机是大事。文章源自JAVA秀-https://www.javaxiu.com/24474.html

OSGI 就是利用自定义的类加载器机制来完成模块化热部署,而它实现的类加载机制就没有完全遵循自下而上的委托,有很多平级之间的类加载器查找,具体就不展开了,有兴趣可以自行研究一下。文章源自JAVA秀-https://www.javaxiu.com/24474.html

这就是第三次破坏。文章源自JAVA秀-https://www.javaxiu.com/24474.html

第四次破坏

在 JDK9 引入模块系统之后,类加载器的实现其实做了一波更新。文章源自JAVA秀-https://www.javaxiu.com/24474.html

像扩展类加载器被重命名为平台类加载器,核心类加载归属了做了一些划分,平台类加载器承担了更多的类加载,上面提到的  -Xbootclasspath、java.ext.dirs 也都无效了,rt.jar 之类的也被移除,被整理存储在 jimage 文件中,通过新的 JRT 文件系统访问。文章源自JAVA秀-https://www.javaxiu.com/24474.html

当收到类加载请求,会先判断该类在具名模块中是否有定义,如果有定义就自己加载了,没的话再委派给父类。文章源自JAVA秀-https://www.javaxiu.com/24474.html

关于 JDK9 相关的知识点就不展开了,有兴趣的自行查阅。文章源自JAVA秀-https://www.javaxiu.com/24474.html

所以这就是第四次破坏。文章源自JAVA秀-https://www.javaxiu.com/24474.html

其他注意点

首先,虽说是子类父类,但是加载器之间的关系不是继承,而是组合。文章源自JAVA秀-https://www.javaxiu.com/24474.html

读者美团五面:Java历史上有三次破坏双亲委派模型,是哪三次?文章源自JAVA秀-https://www.javaxiu.com/24474.html

看下代码就很清晰了,具体的逻辑如下:文章源自JAVA秀-https://www.javaxiu.com/24474.html

读者美团五面:Java历史上有三次破坏双亲委派模型,是哪三次?文章源自JAVA秀-https://www.javaxiu.com/24474.html

在 JVM 中,类的唯一性是由类加载器实例和类的全限定名一同确定的,也就是说即使是同一个类文件加载的类,用不同的类加载器实例加载,在 JVM 看来这也是两个类。文章源自JAVA秀-https://www.javaxiu.com/24474.html

所以说类加载器还有命名空间的作用,我记得这个知识点也是一个面试题哟~文章源自JAVA秀-https://www.javaxiu.com/24474.html

继续阅读
速蛙云 - 极致体验,强烈推荐!!!购买套餐就免费送各大视频网站会员!快速稳定、独家福利社、流媒体稳定解锁!速度快,全球上网、视频、游戏加速、独立IP均支持!基础套餐性价比很高!这里不多说,我一直正在使用,推荐购买:https://www.javaxiu.com/59919.html
weinxin
资源分享QQ群
本站是JAVA秀团队的技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
沙海
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定