来源:IT之家 发表时间:2022-10-11 17:48 阅读量:11147
我是一个名为FlashObject.java的. java文件,就叫我小扎吧。
public class flashobjectprivatestringname,私人占有,publicstringgetnametreturnname,publicintaddreturn a+b,
我正要被JVM虚拟机老板加载和运行,这时老徐走了过来。
徐:小渣渣,我马上就把你送走你先瘦下来,不要占太多空间
小扎:好,没问题,等我十秒钟。
公共类FlashObjectprivate私有字符串名称,私人年龄,public int add return a+b,
小扎:老徐,我瘦了看一看
徐:...,你生病了。
小扎:怎么了。我去掉了无用的空格和回车,瘦了很多!
徐:好吧,看你智商,我给你解释现在你还是一个文本文件给你瘦身就是设置一个紧凑的数据结构来表示你Java文件中的信息,然后告诉我这个数据结构中的每个字节代表什么
小扎:哦,哦,我知道了。
徐:可以,方便我装其次,我的虚拟机不仅服务于你的Java语言,很多语言都可以转换成我的虚拟机能识别的那种你必须设计一个通用格式
小扎:嗯嗯,这次我明白了!
一级信息
我的类名是FlashObject。
先找个地方存放,放下脑袋。
这里的一个小方块就是1个字节,也就是8位一个英文字母用ASCII码表示为1个字节,所以占了一个正方形,后面就不解释了
我严谨地想到,这个类应该有它的父类。
虽然这里面没有写java文件,它也有自己默认的父类Object
当然,我们必须记录完整的班级名称。
Java/语言/对象
你把它写在哪里跟着班名走就行了
呃,不对,我的类名,我的父类名,都变长了,所以挨着放谁知道分界点在哪里
不,我们要在对方前面加一个长度,所以用两个字节来表示就好了。
除了父类,还有接口名!虽然我们没有写这个类,但是我们必须定义它。
这个接口与类名和父类名略有不同,因为可能不止一个。
但这不是问题首先用两个字节来表示接口的数量,然后接口名还是像上面那样一个挨着一个排列
很好。
2常量池
慢慢的,我发现越来越多的地方需要字符串名。
除了前面的类名,父类名,接口名,还有属性名,方法名,属性类名,方法参数类型名,返回值类型名等等。
一方面,如果所有东西都这样写,文件格式会很乱,很多结构会变长。
另一方面,很多字符串是重复的,比如属性名的类名字符串,方法getName的返回值的类名字符串写两遍会浪费空间
所以我决定废除之前的方案,设计一个新的结构来统一存储这些字符串我把它命名为恒常池
每个字符串都有一个与之对应的索引,不需要额外的字段就可以计算出来。
这样,just类,父类和接口都可以指向这个索引,所以长度可以固定。
当然,这个常量池只存储字符串。
不难想到,可能会有整型和浮点型的值作为常量,甚至是一个引用类型,然后这个引用类型再次指向常量池中的一个索引,有点像指针对指针的指向。
这么多类型,一定有地方记录类型信息看来我们得改变以前的设计了
这样我们的常量池不仅可以存储简单的字符串常量,还可以根据不同的类型存储相应数据结构的值。
当然,我们的常量池的整体结构保持不变,但内部有丰富的结构类型。
同样,我们的整个设计也没有受到常量池的微小变化的影响。
好了,总结一下我们目前的整体计划。
在开头存储常量池,然后把你需要的常量都放在这里,用索引指向它。
在存储了类本身的相关信息后,我们立即存储当前类,父类和接口的信息。
看来老徐要求的瘦身工作已经初具规模。
3个变量
既然类本身的信息已经找到了合适的存储位置,那就来存储变量吧。
也可能有多个变量,所以结构还是按照我们之前的思路建模,从变量个数开始,然后是每个变量的数据结构。
至于用什么数据结构存储变量,是不是定长,那是我们接下来要设计的。
让我们取出其中一个变量,看看它有什么。
私有字符串名称,
非常清楚private是变量的标记,String是变量类型,name是变量名
先看标记的部分。
除了私有,还有公共的,受保护的,静态的,最终的,易变的,瞬态的等等。有些可以放在一起,比如
公共静态最终字符串名称,
有些不能放在一起,比如
公共私有字符串名称,//错误
我们用位图的方法,每个标记用一个位来表示,这样无论怎么排列组合,最后的值都不一样。
我们设计并记录与这些标记相对应的值。
标记
公众的
0x0001
私人的
0x0002
保护
0x0004
静电
0x0008
最后的
0x0010
不稳定的
0x0040
短暂的
0x0080
复合标签,可以添加,比如public static,就是0x0001+0x0008 = 0x0009。
但是这种赋值方法,有不同的排列组合,没有重复,也可以很容易的根据值推导出标签。
很好,就这样。
哦,对了,班级信息本身也有这些标注的公私属性。刚记录班级信息的时候,忘了先加,免得以后忘了!
再次查看类型部分。
当前类型是String,它属于引用数据类型中的类类型。
私有字符串名称,
此外,还有八种基本数据类型,以及引用类型中的数组类型。
为了占用更少的空间,我们用最少的符号来表示。
符号表示
类型
字节
茶
两倍
漂浮物
国际组织
长的
短的
布尔型
LClassName
排列
这里的基本数据类型和数组类型只占一个char,只占一个字节。
如果是类,则占用L和,两个字节,加上完整类名占用的字节数。
例如,这里用符号表示的字符串类型是
ljava/lang/String,
但是注意,这里的所有符号都可以存储在常量池中,我们的变量结构的类型描述符部分只需要一个常量池索引。
好了,第二部分也做完了。
看名字部分。
名字的部分没什么好说的相信你能直接猜到直接上图
好了,两个字节的标签,两个字节的类型描述符和两个字节的变量名,这是我们其中一个变量的数据结构。
放在我们最后的总览里。
搞定了。
4种方法
也可能有很多方法我目前只有两种方法我们就拿加法来分析吧
publicintadtreturna+b,
当然,更确切的说,我还有一个构造方法没写。
总之,可能有很多。
但是有了设计变量的经验,方法的数据结构很快就成型了。
标记部分和变量标记部分一样,有相同的值,所以我们可以给它们一个值。
标记
公众的
0x0001
私人的
0x0002
保护
0x0004
静电
0x0008
最后的
0x0010
不稳定的
0x0040
短暂的
0x0080
同步的
0x0020
当地的
0x0100
摘要
0x0400
方法描述符指的是方法参与的返回值,比如我们的:
int add
参与返回值的类型的符号表示和上面变量类型的符号表示完全一样,只是多了一个void类型。
符号表示
类型
字节
茶
两倍
漂浮物
国际组织
长的
短的
布尔型
LClassName
排列
空的
因为有多种参数类型,所以需要设置一个整体的格式,整个描述符的格式是:
返回值类型
像我们一样。
int add
它表示为
我
是不是很精简同样,这是一个字符串,也可以存放在常量池中,这里就不赘述了
这个名字,我们都很熟悉,放了恒池!
好了,前三个说完了最后一个,很有意思
代码,异常,注释等如您所见,需要记录的信息相当多
比如我这样写。
RequestMingpublicStringfunctionthrowsexception return,
将有代码部分,异常,评论和其他信息输入。
但是好像除了代码部分,其他部分都不是每个方法都有的如果都定义了,岂不是浪费篇幅
遵循常量池的惯例,我们把这些部分称为方法的属性一个方法可以有多个属性,设计结构如下
这样,方法有什么属性呢按需添加即可如果不需要这个属性,就不用浪费空间了
回顾我们的方法。
publicintadtreturna+b,
刚才方法的签名部分已经解决了,只剩下代码了。
返回a+b,
你如何储存这个。
正如老徐之前所说,JVM识别称为字节码的东西,所以我想把用Java语言编写的代码转换成字节码。
这部分很复杂,就不展开说我的过程了经过一番努力,我将这一行简单的代码转换成了字节码
1B 1C 60 AC
总共有四个字节。
我把这四个字节放在刚才代码类型的属性里。
好了,你完成了。
回过头来,我们将完成前面的方法。
将这种结构添加到我们的全球结构中。
完美!
5,阶级
我把自己改造成这样一个结构,带着这个最终的设计草稿,我去了老徐。
徐:嗯!还不错!
小扎:当然,我研究了很久了。
徐:不过,我给你改一下,在开头加些东西。
小扎:老徐,你加了什么。
徐:你一看就没有经验。
一般用幻数来标识这个文件的格式,但是用文件名的后缀是不靠谱的一般带格式的文件都会有一个幻数
后两个用于识别版本号不同的版本可能有不同的数据结构和支持的功能
小扎:我明白了,还是你总是满腹经纶但是你说用来识别这个文件的格式我的这个文件是什么
徐:你这个笨蛋,就叫它班档!
FlashObject.class
附言
根据Java虚拟机规范,Java虚拟机规范Java SE 8版,一个类文件的标准结构是这样的。
class file u 4 magic,u2minor _ versionu2major _ versionu2constant _ pool _ countcp _ infoconstant _ poolu2access _ flagsu2this _ classu2super _ classu2interfaces _ countU2接口,u2fields _ countfield _ info fields,u2methods _ countmethod_infomethods,u2attributes _ countattribute_infoattributes,
我们的设计和它几乎一样。
只有后两项,我们没有涉及到,本身并不是重点。
常量池中的类型如下。
constant type value constant _ class 7 constant _ field ref 9 constant _ method ref 10 constant _ interfacemethodref 11 constant _ string 8 constant _ integer 3 constant _ float 4 con STANT _ long 5 constant _ double 6 constant _ name and type 12 constant _ utf 81 constant _ method handle 15 constant _ method type 16 constant _ invoked ynamic 18
如果你想知道类文件的所有细节,最好的方法是阅读官方文档,这是Java虚拟机规范的第四部分。
第四章。类文件格式
此处的链接可以直接找到:
不要觉得公文晦涩难懂,这部分还是很清楚的大部分博客对格式的解释基本都很短,不生动不如直接看公文
另一个好方法是直接观察类文件的二进制结构分析这里推荐一个工具
班级
使用此工具打开一个类文件,如下所示。
左边解析的树形结构可以直接对应右边类文件的二进制内容,非常好用。
最后,希望你能抽时间用这个工具分析一个复杂的类文件,会很有帮助祝大家都学好班级文件
完~
郑重声明:此文内容为本网站转载企业宣传资讯,目的在于传播更多信息,与本站立场无关。仅供读者参考,并请自行核实相关内容。