解析Class文件的数据结构

Class文件是一组以8位字节为基础单位的二进制流,只有两种数据类型:无符号数和表

u1、u2、u4、u8分别代表1个字节、2个字节、4个字节、8个字节的无符号数。

参考书《深入了解JAVA虚拟机:JVM高级特性与最佳实践(第2版)》  周志明著

下面将按顺序解析Class文件的数据结构

魔数和Class文件的版本:

每个Class文件的头4个字节称为魔数,唯一作用是确定这个文件是否为一个能被虚拟机接收的Class文件。P166

Class文件的版本:第5和第6个字节是次版本号,第7和第8个字节是主版本号。P166

常量池:

首先是一项u2类型数据代表常量池容量计数值(计数是从1开始,其他都是从0开始)P168

接着是一张张常量表:

      第一位是一个u1类型的标志位(tag,代表常量的类型)         P169 表6-3

      之后对于不同常量有不同常量表的结构(P172 表6-6),大小位u2

             注意对于int、double等java基本数据类型,是直接用二进制表示其值,按照最高位在前的存储(P164 下面注释)                                        对于Class_info这种类型的常量,往往有index索引指向类型为Utf8_info的常量,代表其类的全限定名(例com/TestClass)

 

访问标志:

u2类型的访问标志(access_flags,用以识别一些类或接口的信息,通俗讲就是识别类或接口名之前的java关键词,P173 表6-7),如public 标志值是0x0001,有父类是0x0020,一个普通public修饰的类,access_flags值便是两个标志值相加,即0x0021

 

类索引、父类索引与接口索引集合: P174

类索引、父类索引都是u2类型(因为类和父类最多只能有一个),接口索引集合是u2类型的数据集合(接口可以多继承)。类索引确定这个类的全限定名,父类索确定这个类的父类的全限定名(除Object以外都有父类)。每一索引值都是代表指向常量池的计数值(注意前面说到了从1开始),指向的索引可能还会指向常量池其他的常量。 

例如:常量1是Class类型,index索引指向常量2

           常量2是utf8类型,值通过解码得到是com/TestClass

           常量3是Class类型,index索引指向常量4

           常量4是utf8类型,值通过解码得到是java/lang/Object

           那么该类索引值是0x0001,父类索引值是0x0003                        

对于接口索引集合:

       第一项是u2类型的数据为接口计数器(设为x,表示索引表的容量)

       后面接着是x*2个字节的接口索引,x可能为0。

 

字段表集合:用来描述接口或类中声明的变量。

首先是一个u2类型的数据为容量计数器(fields_count),其值直接代表有几个字段,对于每个字段都遵循字段表结构

字段表结构(P176 表6-8
类型 名称  数量 解释
u2 access_flags 1 字符修饰符,与访问标志中的access_flags非常相似 (P176 表6-9
u2 name_index 1 字段简单名称,test()方法的简单名就是test,m字段的简单名就是m,其值指向常量池的常量的编号
u2 descriptor_index 1 字段和方法的描述符,描述其代表的数据类型,java每个数据类型(包括对象和void)都对应了标识符(P177 表6-10),descriptor_index其值代表常量池中常量的标号,例如,其值时0x0006,第6个常量是utf8类型,解码得到D,表示这是一个double类型。描述方法时,先参数列表,后返回值
u2 attributes_count 1 额外属性表集合的数量
attribute_info attributes attributes_count 属性表集合

字段表集合不会列出从超类或者父接口中继承而来的字段。

 

方法表集合:与字段表集合类似,用来描述接口或类中声明的方法。

首先是一个u2类型的数据为容量计数器(methods_count),其值直接代表有几个方法,对于每个方法都遵循方法表结构

方法表结构(P178 表6-11
类型 名称  数量 解释
u2 access_flags 1 方法修饰符,与访问标志中的access_flags非常相似 (P179 表6-12
u2 name_index 1 名称索引
u2 descriptor_index 1 描述符索引,先参数列表,后返回值(P177 表6-10),例如,方法 java.lang.String test(double[] a, int b, char[][] c, int d)的描述符为“([DI[[CI)Ljava/lang/String;”
u2 attributes_count 1 额外属性表集合的数量
attribute_info attributes attributes_count 属性表集合

同样父类方法也不会出现,但是编译器可能会自动添加方法,典型的是类构造器"<clinit>"和实例构造器“<init>”

 

属性表集合:

只要满足基本的属性表结构,可以自定义属性表。属性表是根据字段和方法后有无属性,有的话属性表集合的字节码接在对应的后面。

属性表结构(P182 表6-14)
类型 名称  数量 解释
u2 attribute_name_index 1 该值为对应属性名所代表的常量的编号,例如,方法里的代码编译的字节码存放在一个属性名为“Code”的属性中,0x0009即9号常量的值为"Code"
u4 attribute_length 1 属性值所占的位置(最多是2^32-1)
u1 info attribute_length 自定义

《Java虚拟机规范(Java SE7)》版中预定义了21项属性。(P180 表6-13

1. Code属性:Java程序方法体中的代码经过Javac编译器处理后,最终变成字节码指令存储在Code属性内。

       Code属性表结构(P182 表6-15

 

 

猜你喜欢

转载自blog.csdn.net/talkeverything/article/details/82870864