Class 文件结构
反汇编:javap -c xxxx.class
使用 -v
可以多打印一些信息
Java 技术能够一直保持非常好的向后兼容性,这点 Class 文件结构的稳定性功不可没。class 文件结构的内容,绝大部分在 JDK1.2 时代就已经定义好了。每次基本上只是在原有结构基础上新增内容、扩充功能,并未对定义的内容做修改。
任何一个Class 文件都对应着唯一一个类或接口的定义信息,但反过来说,Class 文件实际上它并不一定以磁盘文件的形式存在(比如可以动态生成、或者直接送入类加载器中)。Class 文件是一组以 8 位字节为基础单位的二进制流。
Class 文件格式采用一种类似于C 语言结构体的伪结构来存储数据,这种伪结构中只有两种数据类型:无符号数和表。
- 无符号数属于基本的数据类型,以 u1、u2、u4、u8 来分别代表 1 个字节(一个字节是由两位 16 进制数组成)、2 个字节、4 个字节和 8 个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照 UTF-8 编码构成字符串值。
- 表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个 Class 文件本质上就是一张表。
文件结构(按顺序):
No. | 结构 | 说明 |
---|---|---|
1 | magic(u4) | cafe babe ,魔数 |
2 | minor_version(u2) | 次版本号 |
3 | major_version(u2) | 主版本号,从 45 开始;JDK1.8 是52 |
4 | constant_pool_count(u2) | 容量计数是从 1 开始而不是 0 |
5 | constant_pool | 常量池内容 |
6 | access_flags(u2) | 表示某个类或者接口的访问权限和属性 |
7 | this_class(u2) | 类索引,必须是一个 CONSTANT_Class_info 类型的结构体,表示这个 class 文件所定义的类和接口。用于确定这个类的全限定名 |
8 | super_class(u2) | 父类索引,除 java.lang.Objec t 外,都不为 0 |
9 | interfaces_count(u2) | 接口计数器,表示当前类或者接口直接继承接口的数量 |
10 | interfaces(u2) | 接口表,是一个表结构,是对常量池中 CONSTANT_Class_info 类型的一个有效索引值 |
11 | fields_count(u2) | 字段计数器,当前 class 文件所有字段的数量 |
12 | fields | 字段表,是一个表结构,每个成员必须是 filed_info 数据结构,用于表示当前类或者接口的某个字段的完整描述,但它不包含从父类或者父接口继承的字段。但有可能列出原本 Java 代码之中不存在的字段,譬如在内部类中为了保持对外部类的访问性,会自动添加指向外部类实例的字段。描述接口或者类中声明的变量。字段(field)包括类级变量以及实例级变量。而字段叫什么名字、字段被定义为什么数据类型,这些都是无法固定的,只能引用常量池中的常量来描述。 |
13 | methods_count(u2) | 方法计数器,表示当前类方法表的成员个数 |
14 | methods | 方法表,是一个表结构,每个成员必须是 method_info 数据结构,用于表示当前类或者接口的某个方法的完整描述描述了方法的定义,但是方法里的 Java 代码,经过编译器编译成字节码指令后,存放在属性表集合中的方法属性表集合中一个名为“Code”的属性里面。 与字段表集合相类似的,如果父类方法在子类中没有被重写(Override),方法表集合中就不会出现来自父类的方法信息。 但同样的,有可能会出现由编译器自动添加的方法,最典型的便是类构造器 <clinit> 方法和实例构造器<init> |
15 | attributes_count(u2) | 属性计数器,表示当前 class 文件 attributes 属性表的成员个数 |
16 | attributes | 属性表,是一个表结构,每个成员必须是 attribute_info 数据结构,这里的属性是对 class 文件本身,方法或者字段的补充描述,比如 SourceFile 属性用于表示 class 文件的源代码文件名存储 Class 文件、字段表、方法表都有自己的属性表集合,以用于描述某些场景专有的信息。如方法的代码就存储在 Code 属性表中。 |
常量池(constant_pool)
常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。
字面量比较接近于 Java 语言层面的常量概念,如文本字符串、声明为 final 的常量值等。
而符号引用则属于编译原理方面的概念,包括了下面三类常量:
类和接口的全限定名(Fully Qualified Name)、字段的名称和描述符(Descriptor)、方法的名称和描述符
类型 | tag(u1) | 内容 |
---|---|---|
CONSTANT_Utf8_info |
1 | length(u2) UTF-8 字符串占用的字节数 bytes(u1) 长度为 length 的字符串 |
CONSTANT_Integer_info |
3 | bytes(u4) 按照高位在前存储的 int 值 |
CONSTANT_Float_info |
4 | bytes(u4) 按照高位在前存储的 float 值 |
CONSTANT_Long_info |
5 | bytes(u8) 按照高位在前存储的 long 值 |
CONSTANT_Double_info |
6 | bytes(u8) 按照高位在前存储的 double 值 |
CONSTANT_Class_info |
7 | index(u2) 指向全限定名常量项的索引 |
CONSTANT_String_info |
8 | index(u2) 指向字符串字面量的索引 |
CONSTANT_Fieldref_info |
9 | index(u2) 指向声明字段的类或者接口描述符CONSTANT_Class_info 的索引项index(u2) 指向字段描述符 CONSTANT_NameAndType_info 的索引项 |
CONSTANT_Methodref_info |
10 | index(u2) 指向声明方法的类描述符CONSTANT_Class_info 的索引项index(u2) 指向名称及类型描述符 CONSTANT_NameAndType_info 的索引项 |
CONSTANT_InterfaceMethodref_info |
11 | index(u2) 指向声明方法的接口描述符CONSTANT_Class_info 的索引项index(u2) 指向名称及类型描述符 CONSTANT_NameAndType_info 的索引项 |
CONSTANT_NameAndType_info |
12 | index(u2) 指向该字段或方法名称常量的索引 index(u2) 指向该字段或方法描述符常量项的索引 |
CONSTANT_MethodHandle_info |
15 | reference_kind(u1) 值在1~9之间,他决定了方法句柄的类型。方法句柄类型的值表示方法句柄的字节码行为 reference_index(u2) 值必须是对常量池的有效索引 |
CONSTANT_MethodType_info |
16 | descriptor_index(u2) 值必须是堆常量池的有效索引,常量池在该索引下的项必须是 CONSTANT_Utf8_info 接口,表示方法的描述符 |
CONSTANT_InvokeDynamic_info |
18 | bootstrap_method_attr_index(u2) 值必须是对当前 Class 文件中引导方法表的 bootstrap_methods[] 数组的有效索引name_and_type_index(u2) 值必须是对当前常量池的有效索引,常量池在该索引出的项必须是 CONSTANT_MethodType_info 结构,表示方法名和方法描述符 |