Class文件是一组以8个字节为基础单位的字节流,各个项目严格按照顺序排列,中间没有任何的分隔符。当遇到需要8个字节以上空间数据项时,则需按照高位在前的方式分割成若干个8个字节进行存储。un表示n个无符号数,table表示多个无符号数组成的表。
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_Class;
u2 super_Class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
魔数与Class文件的版本
每个Class文件的头四个字节被称为魔数(Magic Number),它唯一的作用是确定这个文件是否为一个能被虚拟机接受的Class文件。Class文件的魔数值为)0XCAFEBABE(咖啡宝贝)。紧接着魔数的四个字节是Class文件的版本号。第5、6字节是次版本号(Mnior Version),第7、8字节为主版本号(Major Version)。java的版本号是从45开始的,低版本的class文件可以在高版本的jdk上运行,但是反过来则不行,判断的就是这个版本号。
常量池
紧接着魔数与Class文件的是常量池。常量池的入口是一个u2类型的数据,代表的是常量池常量容量计数值。第0项常量是空的,当后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池的项目”的时候,可以把索引值设置为0来表示。
常量池中主要存放两种常量:字面量(Literal)和符号引用(Symbolic References)。字面量比较接近java的常量概念,主要是文本字符串和被声明为final的常量值等。而符号引用则是编译原理方面的概念。符号引用主要包括如下几个方面:被模块导出或者开放的包,类和接口的全限定名,字段的名称和描述符,方法的名称和描述符,方法句柄和方法类型,动态调用点和动态常量。
常量池中的每一项常量都是一个表。每个表的开头都是一个u1标志位tag,表示当前的常量属于哪种常量。每一种常量有不同的结构,需要根据标志位计算常量类型,在根据常量类型确定常量的内容,所有的17种常量类型结构如下:
访问标志
在常量池之后,紧接着是一个u2,代表访问标志。这个标志用于识别一些类或者接口层次的访问信息。包括:这个是类还是接口,是否定义为abstract,是否定义为public,如果是类的话,是否被声明为final等。
类索引、父类索引与接口索引集合
类索引和父类索引都是一个U2类型数据,而接口索引集合是一组u2数据。class中由这三项数据确定该类型的继承关系。类索引用来确定这个类的全限定名,父类索引用来确定这个类的父类的全限定名。类索引和父类索引都指向一个类型为CONSTANT_class_inf的类描述符常量。通过CONSTANT_class_info类型常量中的索引值可以找到CONSTANT_utf8_info类型中的全限定名字符串。
字段表集合
字段表集合用于描述类或者接口中声名的变量。
java语言中的字段包括类级别的变量以及实例级别变量,但不包括在方法内部声名的局部变量。
字段表结构如下:
类型 | 名称 | 数量 |
---|---|---|
u2 | access_falg | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
方法表集合
与字段表集合类似
属性表集合
属性表结构如下:
类型 | 名称 | 数量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u1 | info | attribute_length |