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

news2024/11/27 17:47:13

前言:

接着上一次02-为什么dex文件比class文件更适合移动端?的继续往下,距离上一篇已经过去快半年了,从我的博文记录中就可以清楚地看到:

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

题面解析:

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

ClassLoader:

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

1、它是做什么的?

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

3、它把class加载给谁?

双亲委派模型:

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

总结:

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

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

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

本题得分点:

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

知识储备:

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

技术思考:

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

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

字节码加载:

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

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

方法区:

那方法区主要是作用是啥呢?其实就是在内存中,存放class文件的逻辑结构,也就是类的元(meta)信息, 其中就包括在上一次02-为什么dex文件比class文件更适合移动端?所学习到的:常量池、类信息、字段、方法、属性等。

方法区实现:

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

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

Java程序的双亲委派模型:

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

关于这块的介绍在之前类加载器双亲委托机制详解已经详细有说明,这里就当大概复习一下核心点,对于要注意:从图中貌似看这些加载类是一个父子的树形结构对吧,其实实际不是:

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

其中位于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,关于它的思想可以参考之前学习的这篇:手写热更新阐述tinker实现原理。

解题总结:

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

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

 关注个人公众号,获得实时推送

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/180543.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023英伟达显卡排名天梯图(已更新)

2023英伟达显卡排名天梯图 注意 这里没有更新4070Ti, 它的性能应该在3090和3090ti之间 Kelvin Kelvin 于 2001 年发布,是 Nvidia 千年以来第一个新的 GPU 微架构。最初的 Xbox 游戏机使用带有 Kelvin 微架构的 NV2A GPU。 GeForce 3 和 GeForce 4 系列 GPU 是采用…

nginx启动命令和停止命令

进入nginx的sbin目录下 cd /usr/local/nginx/sbin/ 1、启动nginx ./nginx 2、停止nginx两种方式 #待nginx进程处理任务完毕进行停止。 ./nginx -s quit #先查出nginx进程id再使用kill命令强制杀掉进程。 ./nginx -s stop 3、查看nginx端口 ps aux|grep nginx 4、查看ng…

机器学习——正则化线性回归和偏差/方差(Matlab代码实现)

目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨‍💻4 Matlab代码 💥1 概述 本文使用水库水位的变化来实现正则化线性回归,以预测大坝流出的水量。后续本文将对调试学习算法进行一些诊断&#…

2023需要重点关注的四大AI方向

2023需要重点关注的四大AI方向 过去10年,人工智能从实验室走向各行各业,成为一种普遍技术应用于众多领域。根据IDC的数据,2022年全球AI市场规模达到4328亿美元,增长近20%。而Precedence Research预计,到2030年&#xf…

商业模式画布的介绍例子

选自《软件开发权威指南》商业模式画布是指一种能够帮助创业者催生创意,降低猜测,确保他们找对目标用户,合理解决问题的工具。商业模式画布不仅能够提供更多灵活多变的计划,而且更容易满足用户的需求。更重要的是,它可…

家居建材商城|基于Springboot+Vue实现家居建材商城

作者主页:编程指南针 作者简介:Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容:Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 收藏点赞不迷路 关注作者有好处 文末获取源…

R|使用ggrepel添加文字标签

最近在用ggrepel&#xff0c;这里记录一些官网教程中的概要。与其去搜答案&#xff0c;不如过一遍软件的示例&#xff0c;大部分的问题都能迎刃而解。更详细的内容可参照官网教程&#xff1a;https://ggrepel.slowkow.com/articles/examples.html>基本用法<相比于geom_te…

python+django健身房课程预约评分系统

启动一个新项目 执行下面的命令来创建一个新的 Django 项目&#xff1a; django-admin startproject myproject 命令行工具django-admin会在安装Django的时候一起自动安装好。 执行了上面的命令以后&#xff0c;系统会为Django项目生成基础文件夹结构。 现在&#xff0c;我…

DFS(深度优先搜索)详解(概念讲解,图片辅助,例题解释,剪枝技巧)

目录 那年深夏 引入 1.什么是深度优先搜索&#xff08;DFS&#xff09;&#xff1f; 2.什么是栈&#xff1f; 3.什么是递归&#xff1f; 图解过程 问题示例 1、全排列问题 2、迷宫问题 3、棋盘问题&#xff08;N皇后&#xff09; 4、加法分解 模板 剪枝 1.简介 2.剪枝…

ASCII表

背景 ASCII&#xff08;American Standard Code for Information Interchange&#xff0c;美国信息互换标准代码&#xff09;是一套基于拉丁字母的字符编码&#xff0c;共收录了 128 个字符&#xff0c;用一个字节就可以存储&#xff0c;它等同于国际标准 ISO/IEC 646。 ASCII…

【R语言数据科学】:多项式回归

【R语言数据科学】:多项式回归 🌸个人主页:JOJO数据科学📝个人介绍:统计学top3高校统计学硕士在读💌如果文章对你有帮助,欢迎✌关注、👍点赞、✌收藏、👍订阅专栏✨本文收录于【R语言数据科学】本系列主要介绍R语言在数据科学领域的应用包括: R语言编程基础、R语…

软件测试该怎么测?10个测试方法,带你初步了解

软件测试该怎么测&#xff1f;10个测试方法&#xff0c;带你初步了解1.需求测试2.界面测试3.功能测试4.安全性测试5.可靠性测试6.可移植性测试7.兼容性8.易用性9.压力测试10.异常场景测试当然还有很多测试方法&#xff0c;这些要根据实际不同应用场景而变化&#xff0c;这里就以…

蓝桥杯算法训练合集五 1.简单字符变换2.字母转换3.输出一个倒等腰三角形4.寻找数组中最大值5.斐波拉契数列6.高低位变换

目录 1.简单字符变换 2.字母转换 3.输出一个倒等腰三角形 4.寻找数组中最大值 5.斐波拉契数列 6.高低位变换 1.简单字符变换 问题描述 输出任意一个小写字母&#xff0c;要求输出其ASCII码&#xff0c;并输出对应的大写字母。 输入格式 从键盘输入小写字母。 输出格式 输…

【c语言进阶】动态内存管理知识大全(上)

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a;c语言学习 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对我…

敢问路在何方?拒绝【内卷】到【进化】的底层逻辑

不知道什么时候&#xff0c; “内卷”这个词频繁出现在各行各业&#xff0c;人们也喜欢把各种问题归结于“内卷”。网络段子都说&#xff0c;以前打招呼是 “你吃了吗”&#xff1f;现在是“你卷赢了吗”&#xff1f; 从【你吃了吗&#xff1f;】到【你卷赢了没&#xff1f;】 …

C++基础(1) - 前导知识

文章目录程序编译流程常用的C编译器各种编译命令程序编译流程 常用的C编译器 最初的 cfront&#xff1b;Unix、Linux 系统中的 GNU g 编译器&#xff1b;Windows 系统中的 Cygwin、MinGW(Minimalist GNU for Windows)、MinGW-w64 等&#xff0c;它们都包含 GNU g 编译器&#…

了解JUnit测试框架

作者&#xff1a;~小明学编程 文章专栏&#xff1a;测试开发 格言&#xff1a;热爱编程的&#xff0c;终将被编程所厚爱。 目录 注解 Test注释 BeforeEach BeforeAll AfterEach AfterAll 断言 assertEquals / assertNotEquals assertTrue / assertFalse 测试的执行…

linux基本功系列之yum实战

文章目录一. yum命令介绍1.1 yum的介绍1.2 yum的优劣势1.3 使用yum的注意事项1.3.1 配置本地yum源1.3.2 配置网络yum源二. 语法格式及常用选项2.1 yum的全部参数2.2 影响yum的配置文件2.3 最常用的yum参数三. 参考案例实战3.1 使用yum进行安装3.2 使用yum升级和更新软件包3.3 软…

Kettle(9):排序记录组件

1 组件介绍 排序组件可以将Kettle数据流中的数据进行排序,可以指定升序、还是降序排列 2 需求 使用Kettle将t_user表中的用户数据,按照年龄升序排序,并将排序后的数据装载到Excel 3 构建Kettle数据流图 效果图

第二章 Java编程基础

第二章 Java编程基础 目录一&#xff0e; Java基本语法1. 基本格式2. 注释3. 标识符4. 关键字5. 常量二&#xff0e; 变量1. 定义2. 数据类型3. 整数类型变量4. 浮点类型变量5. 字符类型变量6. 布尔类型变量7. 类型转换8. 自动提升9. 变量作用域三&#xff0e; 运算符1. 算数运…