首页 > 编程语言 >03-你能不能自己写一个叫做java.lang.Object的类?

03-你能不能自己写一个叫做java.lang.Object的类?

时间:2023-01-26 16:11:52浏览次数:83  
标签:lang 03 java ClassLoader Object 委派 双亲 模型 加载

前言:

接着上一次https://www.cnblogs.com/webor2006/p/16609029.html的继续往下,距离上一篇已经过去快半年了,从我的博文记录中就可以清楚地看到:

转眼2023年新春假期接近尾声了,在这近半年的时间里,其实发生了很多事,有伤心、有焦虑,当然也有开心和希望,其中还经历过人生中最最艰难的时期,还好在朋友的帮助下给抗过来了【关于这些故事这里就不分享了,默默藏在心里就好】。当然最最让我伤心的是,坚持了这么久的博客彻底地被放弃了,当你没有了节奏感之后,再拣回来是需要付出很大的代价的,这不新年来了嘛,必须要有个新气象,新气象打算先从恢复博客开始,这里接着Android面试题的系列章节继续往下,好,费话不多说,直接开干。

题面解析:

关于这道题,从题面来看,很显然是考ClassLoader相关的东东,而一谈到ClassLoader,就会想到它的双亲委派模型,关于这块的基础可以参考我之前所记录的https://www.cnblogs.com/webor2006/p/9016996.html,说实话这块基本学了就忘,所以正好可以借此机会再来复习一下。其中这里有两个加粗的词:“ClassLoader”、“双亲委派模型”,是的,要想答好此题,需要从这俩角度来进行剖析,那都有哪些点呢,下面列一下:

ClassLoader:

对于它,主要需要掌握如下知识点:

1、它是做什么的?

2、它的加载过程是怎么样的?

3、它把class加载给谁?

双亲委派模型:

对于它,则需要从双亲委派模型的“源码”出发,然后再来掌握它的原理。

总结:

当以上两个知识点都掌握之后,接下来还有一些可以说的:
1、说一下ClassLoader和双亲委派模型这样设计的一个好处

2、双亲委派模型它是一种规则,而有些情况下是需要打破这种双亲委派模型的,那如何打破呢?

是不是这么一解析,要想答好这道题,其实不是那么简单的,而如果按照这么一个思路去解答,就会做到有理有据。

本题得分点:

接下来看一下得分点,还是如之前所学习的,从以下两个角度来看:

知识储备:

主要是ClassLoader、方法区、双亲委派模型。

技术思考:

1、双亲委派解决了什么问题?

2、如何打破双亲委派模型?

字节码加载:

先来回顾一下字节码class文件加载的过程, 如下图:

可以看到.class字节码文件是通过ClassLoader来加载到运行时数据区的“方法区”中的。

方法区:

那方法区主要是作用是啥呢?其实就是在内存中,存放class文件的逻辑结构,也就是类的元(meta)信息, 其中就包括在上一次https://www.cnblogs.com/webor2006/p/16609029.html所学习到的:常量池、类信息、字段、方法、属性等。

方法区实现:

我们知道JVM只是一个规范,而方法区其实也只是一个规范,它有不同的实现方式。

  • 在Java8以前的版本,被实现“永久代”,名称与堆中的“年轻代”、“养老代”相对应,和堆一样,同为线程共有,但又没有垃圾回收【这个比较容易理解,因为方法区是存放的类的元(meta)信息,如果它能够被回收,是不是意味着我们就不能创建类的实例了?】,所以又被称为“非堆”。
  • 在Java8以后的版本,被称为元空间(meta space),直接放在本地内存,所以理论上没有大小上限。

Java程序的双亲委派模型:

先来了解Java程序的双亲委派模型,说到“Java程序”,很明显对于Android来说还有它自身的双亲委派模型,这个之后就会谈到,这里先来看下Java程序的双亲委派,先上个图:

关于这块的介绍在之前https://www.cnblogs.com/webor2006/p/9016996.html已经详细有说明,这里就当大概复习一下核心点,对于要注意:从图中貌似看这些加载类是一个父子的树形结构对吧,其实实际不是:

而标红的是三大系统加载类,最底层的是自己自定义的加载类,对于它们加载的职责也是不一样的,简单说明一下:

 

其中位于jdk中的rt.jar熟悉吧,它里面的类就是由Bootstrap ClassLoader来进行加载的,如我们本题所探讨的java.lang.Object这个类。

Android程序的双亲委派模型: 

概述:

好,接下来则来看一下Android的双亲委派模型了,这块也是咱们最关注的,肯定是跟Java有些不同点的,先上张图:

对比Java的双亲委派模型,系统类加载器由三个变成了二个了,其中PathClassLoader类似于Java中的Application ClassLoader,而自定义ClassLoader变成了DexClassLoader了,很明显在Android中加载的是dex文件了,

类图:

在正式看这块源码之前,先来看一下整体的双亲委派的类图,如下:

ClassLoader源码:

findClass():

好,接下来则先从ClassLoader的源码开始分析,这里只分析主流程,一些不相关的直接省略掉有助于程序的理解,先从入口开始:

这个类应该也是普遍比较熟悉的,它的具体实现是由子类来决定的,这样就使得不同层级的ClassLoader就可以实现去不同的位置加载类的需求了,比如启动类加载器就可以加载jre-lib下的class,应用加载器就可以加载我们工程编译的class,

loadClass():

接下来另一个重要方法就是加载类的方法了,先来看一下它的实现:

这块实现也是大家比较熟悉的了,其中很明显是先读取缓存,如果该类已经被加载了则直接返回,如果木有被加载,则会递归调用父类加载器的loadClass:

而如果父类加载器都加载不到该类,则会交由当前的类加载器进行加载:

其双亲委派的机制就源自于这个类加载器的源代码逻辑。

加载自己写的Object类会发生什么?

好,在了解了双亲委派机制之后, 接下来我们就可以来分析一下,如果自己写了一个java.lang.Object的类,会发生啥?

1、自定义DexClassLoader会递归委派给父类加载器进行加载:

也就是:

接下来父类都未加载过,则会递归委派,如下:

2、启动类加载器会加载framework中的Object类:

根据源码,由于启动类加载器已经是顶层加载器,木有父类了,则此时就会交由自身进行加载,而启动类加载器就会加载framework中的Object类了:

3、总结:

通过上面的分析, 是不是发现,有了这个双亲委派模型,就始终都未执行过自定义DexClassLoader的findClass()了,加载的永远是android framework层的class了?所以很明显对于这题的答案就是“能自己写一个java.lang.Object的类,但是永远不会被加载进来,因为java.lang.Object是系统中的类,会被启动类加载。”

Class的双亲委派模型有什么好处?

接下来再来看一下对于有了双亲委派模型之后,它带来的好处:

1、能够对类划分优先级层次关系;

我们自己写的类,永远都是要比系统的类层次要低的。 

2、避免类的重复加载;

一旦加载了某个类,相关的子类加载器就没有机会重复加载这个类了。

3、沙箱安全机制,避免代码被篡改:

系统的核心类库对于外界来说就是一个沙箱,我们无法通过正常的代码来干涉核心类库的执行,同样的自定义类加载器也无法干预我们写的应用程序的代码。

为什么要打破双亲委派模型呢?

对于双亲委派模型有这么多好处,那何时需要打破这种它,下面举几个例子:

1、解决某些版本冲突问题:

比如:

也就是B类加载器加载了某类库的1.0版本,但是呢对于它的子类加载器c必须要使用该库的2.0的版本,如果按照双亲委派的原则,很明显是办不到的,因为C加载器永远没有办法加载2.0的版本了,此时双亲委派模型就需要打破了,可以这么做:

也就是让子类C加载器越过加载器B,直接成为加载器A的子类加载器,此时就可以让C类加载器使用2.0的版本了,当然前提是子类加载器B和C木有过多的关联关系。

注意: 这只是一个理论上可行的方案【也就是在面试时让面试官了解自己是懂类加载器机制的】,实际开发中有更好的方式来解决版本冲突,比如使用gradle的脚本。

2、 热部署(重复加载已经被加载的类):

这个需求很明显就已经违背了双亲委派模型了【因为已经加载的类,是不允许再次加载的】,此时就需要打破双亲委派了,下面具体来看一下这种场景:

比如有一个热部署的类叫Operation,然后做法就是用一个子类加载器C来加载:

注意,此时C加载器不能成为B类加载器直接或间接的子类加载器,此时C加载器就可以重新加载Operation了,另外它只能在子ClassLoader C加载的类范围内生效:

只能说是一个有限的热部署,这里也是仅限面试时可以提一下,而对于Android来说,通常会想到热更新对吧,它那实现就比较复杂,比如Tinker,关于它的思想可以参考之前学习的这篇:https://www.cnblogs.com/webor2006/p/10708754.html

解题总结:

问:你能不能自己写一个叫做java.lang.Object的类?

答:能写,能通过编译,但是不会被加载,其不被加载的原因是由于有类加载的双亲委派模型所决定【这里就可以展开对双亲委派机制的描述了】,而如果想要被加载就需要打破双亲委派模型了【这里就又可以展开打破双亲委派机制的知识点进行阐述了】。

标签:lang,03,java,ClassLoader,Object,委派,双亲,模型,加载
From: https://www.cnblogs.com/webor2006/p/16635668.html

相关文章

  • 【六袆 - Java】在本地搭建easyexcel源码环境;easyexcel提供的demo单元测试用例完美运
    在本地搭建easyexcel源码环境​​EasyExcel​​​​本地环境搭建​​​​最新版本3.1.5,依赖问题,解决方案​​​​junit-读Excel​​EasyExcel本篇从github或官方文档来源参......
  • 【Java】生产者消费者模型
    【Java】生产者消费者模型0x1前言生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加产品,消费者从存储空间......
  • 【Java】阻塞队列
    【Java】阻塞队列什么是阻塞队列?阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这2个附加的操作支持阻塞的插入和移除方法。支持阻塞的插入方法:当队列满时,队列会阻塞......
  • JavaScript学习笔记—Date
    在JS中所有的和时间相关的数据都由Date对象来表示对象的方法(1)getFullYear()返回当前日期的年份(4位)(2)getMonth()返回当前日期的月份(0-11)(3)getDate()返回当前日期的几......
  • JavaScript学习笔记—Math
    工具类为我们提供了数学运算相关的一些常量和方法常量(1)Math.PI圆周率方法(1)Math.abs()求一个数的绝对值(2)Math.min()求多个值中的最小值(3)Math.max()求多个值中的......
  • Java源码解析 Collection<E>
    位置: java.util.Collection<E>interface用途:参数:<E>Iterable<E>子类:接口:方法   int size();  返回这......
  • Java JDK安装文件提取为绿色版(免安装版)
    前言在服务端部署Java项目时,可能会根据项目的环境考虑使用不同的JDK版本,在使用时根据不同项目切换jdk版本很不方便。另外,安装的jdk会自动安装jdk_update,但是我又不想让jdk......
  • LESSON THREE:Java入门环境搭建
    Java入门环境搭建Java如何诞生改进了c与c++的一些难点;1995年诞生;三大版本:JavaSE:标准版(桌面程序、控制台开发、简单游戏...)JavaME:嵌入式开发JavaEE:E企业级开发(web......
  • 【学懂Java】(六)常用类
    一.包装类1.概念包装类是将基本类型封装到一个类中,包含属性和方法,方便对象操作包装类位于java.lang包中2.转换包装类与基本数据类型包装类是将基本数据类型封装成一个类,包......
  • 你知道这个提高 Java 单元测试效率的 IDEA 插件吗
    前言2023年我们公司主抓代码质量,所以单元测试必不可少,而且都写到了年底的绩效目标中了。在考虑如何达成这个目标的过程中,我发现了一个关于单元测试的IDEA插件——SquareTe......