首页 > 代码库 > Class类文件的结构

Class类文件的结构

1、Class文件是一组以8位字节为基础单元的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件中,中间没有任何的分隔符,这使得整个Class文件中存储的内容几乎全部都是程序运行的必要数据,没有空隙存在。当遇到需要占用8位字节以上空间的数据项时,则会按照高位在前的方式分割为若干个8位字节进行存储。

2、按照Java虚拟机规定,Class文件格式采用一种类似C语言结构体的伪机构来存储,这种伪结构中只有两种数据类型:无符号数和表。

3、无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值、或者按照UTF-8编码构成字符串值。

4、表是由多个无符号数或其他表作为数据项构成的复合数据类型,所有表都习惯性地以"_info"结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上就是一张表,它由如下所示的数据项构成。

Class文件格式
类型名称数量
u4  magic1
u2minor_version1
u2major_version1
u2constant_pool_count1
cp_infoconstant_poolconstant_pool_count-1
u2access_flags1
u2this_class1
u2super_class1
u2interface_count1
u2interfacesinterface_count
u2fields_count1
field_infofieldsfields_count
u2methods_count1
method_infomethodsmethods_count
u2attributes_count1
attribute_infoattributesattributes_count

 5、cp_info,常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。字面量比较接近于Java语言层面的常量概念,人文本字符串、被声明为

      final的常量值等。而符号引用则属于编译原理方面的概念,包括了下面三类常量:

  • 类和接口的全限定名(Fully Qualified Name)
  • 字段的名称和描述符(Descriptor)
  • 方法的名称和描述符    

     常量池中的每一项常量都是一个表,共有11种结构各不相同的表结构数据,这11种表都有一个共同的特点,就是表开始的第一位是一个url类型的标志符(tag,取值为1至12,缺少标志位2的数据类型),代表当前这个常量属于哪种常量类型,11种常量类型所代表的具体含义如下:

常量池的项目烈性
类型标志描述
CONSTANT_Utf8_info1UTF-8编码的字符串
CONSTANT_Integer_info3整型字面量
CONSTANT_Float_info4浮点型字面量
CONSTANT_Long_info5长整型字面量
CONSTANT_Double_info6双精度浮点型字面量
CONSTANT_Class_info7类或接口的符号引用
CONSTANT_String_info8字符串类型字面量
CONSTANT_Fieldref_info9字段的符号引用
CONSTANT_Methodref_info10类中方法的符号引用
CONSTANT_InterfaceMethodref_info11接口中方法的符号引用
CONSTANT_NameAndType_info12字段或方法的部分符号引用

   之所以说常量池是最繁琐的数据,是因为这11种常量类型各自均有自己的结构。11种常量池的结构定义总结如下:

常量池中11种数据类型的结构总表
常量项目类型描述
CONSTANT_Utf8_infotagu1值为1
lengthu2UTF-8编码的字符串占用了字节数
bytesu1长度为length的UTF-8编码的字符串
CONSTANT_Integer_infotagu1值为3
bytesu4按照高位在前存储的int值
CONSTANT_Float_infotagu1值为4
bytesu4按照高位在前存储的float值
CONSTANT_Long_infotagu1值为5
bytesu8按照高位在前存储的long值
CONSTANT_Double_infotagu1值为6
bytesu8按照高位在前存储的double值
CONSTANT_Class_infotagu1值为7
indexu2指定全限定名常量项的索引
CONSTANT_String_infotagu1值为8
indexu2指定字符串字面量的索引
CONSTANT_Fieldref_infotagu1值为9
indexu2指定声明字段的类或接口描述符CONSTANT_Class_info的索引项
indexu2指向字段描述符CONSTANT_NameAndType的索引项
CONSTANT_Methodref_infotagu1值为10
indexu2指定声明方法的类描述符CONSTANT_Class_info的索引项
indexu2指定名称及类型描述符CONSTANT_NameAndType的索引项
CONSTANT_InterfaceMethodref_infotagu1值为11
indexu2指向声明方法的接口描述符CONSTANT_Class_info的索引项
indexu2指定名称及类型描述符CONSTANT_NameAndType的索引项
CONSTANT_NameAndType_infotagu1值为12
indexu2指向该字段或方法名称常量项的索引
indexu2指向该字段或方法描述符常量项的索引

    可以使用javap工具的-verbose参数输出TestClass.class文件的字节码内容。

 6、字段表(field_info)用于描述接口或类中声明的变量。字段(field)包括了类级变量或实例级变量,但不包括在方法内部声明的变量。字段表结构如下:

字段表结构
类型名称数量
u2access_flags1
u2name_index1
u2descriptor_index1
u2attributes_count1
attribute_infoattributesattributes_count

7、方法表的结构同字段表一样,仅在访问标志和属性表集合的可选性中有所区别。

方法表结构
类型名称数量
u2access_flags1
u2name_index1
u2descriptor_index1
u2attributes_count1
attribute_infoattributesattributes_count

8、属性表(attribute_info)在Class文件、字段表、方法表中都可以携带自己的属性表集合,用于描述某些场景专有的信息。

     与Class文件中其他数据项目要求严格的顺序、长度和内容不同,属性表集合的限制稍微宽松了一些,不再要求各个属性表具有严格的顺序,并且只要不与已有的属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息,java虚拟机运行时会忽略掉它不认识的属性。为了能正确地解析Class文件,《Java虚拟机规范(第二版)》中预定义了9中虚拟机应当能识别的属性,具体如下所示:

虚拟机规范预定义的属性
属性名称使用位置含义
Code方法表Java代码编译成的字节码指令
ConstantValue字段表final关键字定义的常量值
Deprecated类、方法表、字段表被声明为deprecated的方法和字段
Exceptions方法表方法抛出的异常
InnerClasses类文件内部类列表
LineNumberTableCode属性Java源码的行号与字节码指令的对应关系
LocalVariableTalbeCode属性方法的局部变量描述
SourceFile类文件源文件描述
Synthetic类、方法表、字段表标识方法或字段为编译器自动生成的

  Code属性,Java程序方法体里面的代码经过javac编译器处理之后,最终变为字节码指令存储在Code属性内。Code属性出现在方法表的属性集合之中,但并非所有的方法都必须存在这个属性,譬如接口或抽象方法就不存在Code属性,如果方法表有Code属性存在,那么它的结构将如表所示:

Code属性表的结构
类型名称数量
u2attribute_name_index1
u4attribute_length1
u2max_stack1
u2max_locals1
u4code_length1
u1codecode_length
u2exception_table_length1
exception_infoexception_tableexception_table_length
u2attributes_count1
attribute_infoattributesattributes_count