开发者社区> 挚爱灬> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

[动态代理三部曲:中] - 从动态代理,看Class文件结构定义

简介: 前言 这篇内容是上一篇[动态代理三部曲:上] - 动态代理是如何"坑掉了"我4500块钱的补充,进一步分析篇。 建议二者结合食用,醇香绵软,入口即化。 好了,不扯淡了,开始... 正文 2、Class 文件的格式 这里为啥是2开头呢?因为上篇文章是1 这部分内容不知道各位小伙伴是怎么感觉的。
+关注继续查看

前言

这篇内容是上一篇[动态代理三部曲:上] - 动态代理是如何"坑掉了"我4500块钱的补充,进一步分析篇。
建议二者结合食用,醇香绵软,入口即化。

好了,不扯淡了,开始...

正文

2、Class 文件的格式

这里为啥是2开头呢?因为上篇文章是1

这部分内容不知道各位小伙伴是怎么感觉的。最开始学习的时候,我是一头雾水,不知道如何下手。当一步步结合反射、JVM内存模型,类加载机制后。再回过头来就会发现一起豁然开朗。

此篇内容的开始,让我们根据我们demo中所用的类:RentHouseProcessorHandler来分析这个问题。
如果我们用十六进制编辑器(比如:Sublime)打开这个RentHouseProcessorHandler.class文件:

img_4cde01fbb422ed6aba0ffc491fedb71a.png
十六进制的Class文件

说实话这一行行的文字,最开始我是拒绝的。哦,上帝,为什么要让我看这些鬼东西...其实如果我们静下心来,想当年高中时代学习数学,物理公式那样去认真的对待它。就会发现它不过就是:一堆人为的定义上了含义的符号而已。

在我们准备读懂这些十六进制文字时,先让我们看一幅《Java虚拟机规范(Java SE 7)》对class文件的定义:


img_762c573ad44c424f095b635d1c40d523.png
《Java虚拟机规范(Java SE 7)》

2.1、Class文件的规范结构

2.1.1、标准结构

上图的内容,其实非常的通俗易懂,不要因为是不常见的英文就抵触它们。让我们尝试着去翻译它们:
1、魔数;2、次版本号;3、主版本号;4、常量池数量;5、常量池;6、权限标识;7、此类;8、父类;9、接口数目;10、接口;11、变量数目;12、变量;13、方法数目;14、方法....

img_63945ca50d9c6494720b13f4808e4bfb.png
Class结构

其实是不是发现了什么,这不是就一个类应该存在的东西么?没错啊,Class文件的结构就是固定了我们编写的Class类所存放的规则而已。最开始的我,以为是深奥,没敢去了解他们。当我踌躇满志,鼓足勇气去准备好好大干一场的时候,才发现它太简单了...就是一些规则,仅此而已。

2.1.2、特别注意的结构:表

虽然只是一些规则,但规则之中,总会有一些特别需要我们去注意的地方:比如cp_info这个类型。在《深入理解Java虚拟机》中,作者把以_info结尾的类型称之为“表”。这里让我们也沿用这种表达方式。说白了,它就是拥有多级关系的类型。

cp_info 表示常量池(常量池:首先它和方法区中运行时常量池不是同一个内容。这里的常量池存放了字面量和符号引用)。


符号引用:

符号引用:

  • 类和接口的全局限定名
  • 字段的名称和描述符
  • 方法的名称和描述符

这里符号引用的作用,我们想先一个问题。CPU执行程序的时候,实际上是去寻找对应指令的内存地址。但是我们的Class文件是先被编译出来的,但是此时还没有被JVM加载到内存,所以肯定是不可能存在内存地址这一说的。因此我们的Class文件需要一些标识,让JVM加载内容的时候从常量池中获取到对应的符号引用,然后在映射到具体的内存地址上。


放到常量池的中数据项在《Java虚拟机规范(Java SE 7)》中一共有14个常量,每一种常量都是一个“表”,并且每种常量都用一个公共的tag来表示是哪种类型的常量。具体内容如下图:


img_47d77a483a14fe05dd6b8374200d9947.png
表类型.png

这里让我们先解读一下这个常量池:让我们跳过u4的魔数、u2的此版本、u2的主版本。直接来看constant_pool_count。跳过对应的内容,那么我们的constant_pool_count就对应十六进制的20,对应十进制的32,也就是说常量池中有32个内容?实际不是的,因为设计者将第0个位置空出来另做打算。所以我们的常量池只有31个内容。


img_3e20d4bce0e34011cc793471e251edb4.png
constant_pool_count

我们可以通过javap命令证实这个问题。


img_79d62531faaf7d698b9f222a9194b20c.png
javap

接下来的一个字节:0a,翻译成十进制就是10,对应我们表中的CONSTANT_Methodref_info,而接下来的四个字节。分别代表索引3,20。这里的索引代表什么意思呢?注意理解下图中标红的地方:

img_baf07d71cea41a23deeeda6c7bc541be.png
常量池只有31个内容

接下来就不逐个解读这些内容了,因为它就是一个对应的过程。如果小伙伴们有兴趣可以自行去尝试解读一番哦。推荐一个工具JavaClassViewer,可以比较方便的查看这些内容:


img_863990a599628b57be430f63e583fba9.png
JavaClassViewer-左视图
img_4fe7ea5747d0e4686f9f91dc86073e91.png
JavaClassViewer-右视图

常量池结束之后,便是我们正常的变量,方法的信息。而这里我们需要了解一个全新的概念:描述符。
对于我们来说一个变量、方法在java源码里是什么样子我们很清楚。但是它们在class文件里是什么样子的呢?这个样子其实就被称之为:描述符。

上文谈动态代理的时候,我们了解到了ProxyGenerator.generateProxyClass(proxyName,interfaces, accessFlags);方法中,通过:

dout.writeInt(0xCAFEBABE);
MethodInfo minfo = new MethodInfo("<init>", "(Ljava/lang/reflect/InvocationHandler;)V",ACC_PUBLIC);    

等方法,构建了我们的$Proxy0所需要的class结构,是不是和我们javap出来的内容很类似?接下来让我们走进描述符。

2.2、变量、方法的描述符

经过上述内容的铺垫,0xCAFEBABE是什么意思,应该无需多言了。而<init>是构造方法的意思。再加上(Ljava/lang/reflect/InvocationHandler;)V以及ACC_PUBLIC就可以表示为InvocationHandler的public的构造方法,其中V表示无返回值。

  • <init>:对象构造器方法。
  • <clinit>:类构造器方法。

这里的内容就被称之为方法的描述符,让我们简单的看一些图,加深这方面内容。


img_90178d5e60663c00262cdac6130a4121.png
类型描述符

基本类型和void在描述符中都有一个大写字符和他们对应; 那么引用类型的描述符,又是什么样子的呢?

“L” + 类型的全限定名 + “;”

例如下图中的:Ljava/lang/Object;就是表示这是一个Object类型。

img_d1ec727fb53adaba1222feb72e560501.png
方法对应的描述符

而方法描述符的规则也很简单,上图中,总结出来就是一句话:

(参数类型1参数类型2参数类型3 ...)返回值类型

例如上图中的:int[] m(int i,String s)转换为描述符:(ILjava/lang/String;)[I

不知道截了这么多图,大家对描述符有没有比较明确的认识。说白了我们我们ProxyGenerator.generateProxyClass(proxyName,interfaces, accessFlags);中所write的内容就是具体方法的描述符。

然后通过DataOutputStream转成byte数组,那么就是我们Class文件所固定的内容了。因此,此时我们的Class文件就已经构建完毕,接下来所需要的就是将其加载到内存中,供我们使用。

2.3、收尾

到这准备结束class文件结构的内容。不知道小伙伴们是否有收获。因为篇幅是在有限,有些内容又不是一句话俩句话可以描述清楚的。所以有些内容一带而过,实在抱歉。具体细节内容,大家可以参考《深入理解Java虚拟机》。

希望大家可以谅解。

我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,已经我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~

img_89788b3a8f3f86257453cbc8264959f6.png
个人公众号:IT面试填坑小分队

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
最通俗易懂的Class文件结构(下)
书接上一回,分享了Class文件的主要构成,同时也详细分析了魔数、次版本号、主版本号、常量池集合、访问标志的构造,接下来我们就继续学习。
50 0
最通俗易懂的Class文件结构(上)
在Java刚刚诞生的时候就提出了一个非常著名的口号:“一次编写,到处运行。(Write Once,Run Anywhere)”。为了实现平台无关性,各种不同平台的虚拟机都统一使用一种程序储存格式,就是字节码(ByteCode)。它就以二进制字节流的方式
45 0
JVM的class文件结构详解(三)
JVM的class文件结构详解(三)
33 0
Class文件结构介绍[魔数版本号]
对于每个java程序员来说class文件应该是每天都会接触的,一个class文件都对应着唯一的一个类或接口的定义信息,但是对应class文件的具体存储结构并不一定很清楚
65 0
Class类文件结构概述
参考文献:周志明《深入理解Java虚拟机》第二版 关于Class文件 class 文件应该是所有 Java 程序员都必定知道的文件,因为我们的 Java 源代码经过 javac 编译以后就会得到这个 class 文件。
1211 0
YII文件上传
&lt;span style="font-size:14px;"&gt;use yii\web\UploadedFile; public function actionDoartadd(){ //获取session $session = Yii::$app-&gt;session; $session-&gt;open(
1260 0
C/C++ 读取16进制文件
1.为什么有这种需求   因为有些情况需要避免出现乱码。不管什么编码都是二进制的,这样表示为16进制就可以啦。 2.如何读取16进制文件   最近编程用这一问题,网上查了一下,感觉还是自己写吧。   16进制数据一般是:text=0x340xb5...,就是0x开头,之后是两个数字十六进制数。
1036 0
Yii自定义配置文件存放方法
在项目的main.config文件中的 'params' => array( 'adminEmail' => 'webmaster@example.com', ), 就是自定义的配置文件 不过这样的方式不容易修改 推荐的方法是 'params' => require('params.
727 0
+关注
挚爱灬
2018年毕业生,目前在人人车任职android开发工程师
24
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载