00.Class文件的组成结构

Class文件的组成结构

Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件中,中间没有任何分隔符。Java虚拟机规范规定Class文件采用一种类似C语言结构体的伪结构来存储数据,这种伪结构中只有两种数据类型:无符号数和表,我们之后也主要对这两种类型的数据类型进行解析。

  • 无符号数: 无符号数属于基本数据类型,以u1、u2、u4、u8分别代表1个字节、2个字节、4个字节和8个字节的无符号数,可以用它来描述数字、索引引用、数量值或utf-8编码的字符串值。
  • 表: 表是由多个无符号数或其他表为数据项构成的复合数据类型,名称上都以 _info 结尾。

Class文件的头8个字节

Class文件的头8个字节是魔数和版本号,其中头4个字节是魔数,也就是 0xCAFEBABE,它可以用来确定这个文件是否为一个能被虚拟机接受的Class文件(这通过扩展名来识别文件类型要安全,毕竟扩展名是可以随便修改的

4个字节则是当前Class文件的版本号,其中第56个字节是次版本号,第78个字节是主版本号。

常量池

从第9个字节开始,就是常量池的入口,常量池是Class文件中:

  • 与其他项目关联最多的的数据类型;
  • 占用Class文件空间最大的数据项目;
  • Class文件中第一个出现的表类型数据项目。

常量池的开始的两个字节,也就是第910个字节,放置一个u2类型的数据,标识常量池中常量的数量cpc (constant_pool_count),这个计数值有一个十分特殊的地方,就是它是从1开始而不是从0开始的,也就是说如果cpc = 22,那么代表常量池中有21项常量,索引值为1 ~ 21,第0项常量被空出来,为了满足后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”时,将让这个索引值指向0即可。

常量池中记录的是代码出现过的所有token(类名,成员变量名等,也是我们接下来要修改的地方)以及符号引用(方法引用,成员变量引用等,主要包括以下两大类常量:

  • 字面量: 接近于Java语言层面的常量概念,包括
    • 文本字符串
    • 声明为final的常量值
  • 符号引用: 以一组符号来描述所引用的目标,包括
    • 类和接口的全限定名
    • 字段的名称和描述符
    • 方法的名称和描述符

常量池中的每一项常量都通过一个表来存储。目前一共有14种常量,不过麻烦的地方就在于,这14种常量类型每一种都有自己的结构,我们在这里只详细介绍两种:CONSTANT_Class_infoCONSTANT_Utf8_info

CONSTANT_Class_info的存储结构为:

... [ tag=7 ] [ name_index ] ...
... [  1  ] [     2    ] ...

其中,tag是标志位,用来区分常量类型的,tag = 7就表示接下来的这个表是一个CONSTANT_Class_infoname_index是一个索引值,指向常量池中的一个CONSTANT_Utf8_info类型的常量所在的索引值,CONSTANT_Utf8_info类型常量一般被用来描述类的全限定名、方法名和字段名。它的存储结构如下:

... [ tag=1 ] [ 当前常量的长度 len ] [ 常量的符号引用的字符串值 ] ...
... [  1  ] [        2        ] [         len         ] ...

后面还没看,先简单介绍到这里……

下一页