Class文件结构
前置概念
Class文件的基本数据类型是由无符号数和表组成的。
- 无符号数是基本类型,可取的值有 u1,u2,u4,u8分别代表了1,2,4,8个字节的无符号数,这种数据可以用来表述数字、索引引用、数量值或者按照UTF-8编码构成的字符串值。
- 表则相当于一种数据结构,表中的每一项可以是一种基本类型,也可以是另一个表。所有的表都习惯性的以"_info"结尾。
- 全限定名,也就是一个类的全路径
- 简单名,例如一个类中的add()方法,它的简单名就是add
- 描述符
- 字段描述符,用来描述字段的类型。例如"S",代表short类型,"I"代表int类型等。
- 方法描述符,用来描述方法的参数列表和返回值。例如”(IS)I“则代表这个方法需要两个参数,一个int,一个short,返回值是一个int类型。
一、魔数和Class文件的版本号
- ”CAFEBABE“
- 类型:u2;Class文件的开头4字节为固定的”CAFEBABE“表明当前的文件是一个Class文件
- minor_version
- 类型:u2;当前class文件的次版本号
- major_version
- 类型:u2;当前class文件的主版本号
次版本号和主版本号规定了当前的class文件可以被哪个版本的虚拟机所执行,同时虚拟机加载class文件的时候也会检查这三个属性。
二、常量池
-
constant_pool_count
- 类型:u2;常量池中的常量数量,由于常量池中的常量数量不确定,所以需要这个字段告诉虚拟机当前类中有多少个常量,而后则是这一个一个的常量,每个常量的类型可能不一致,但都会明确自己的数据结构和占用空间。
-
constant_pool
-
类型:cp_info;常量池中的一项常量,可以是下面14中类型的其中一种。
标志 类型 描述 1 CONSTANT_Utf8_info UTF8编码的字符串 3 CONSTANT_Integer_info 整型字面量 4 CONSTANT_Float_info 浮点型字面量 5 CONSTANT_Long_info 长整型字面量 6 CONSTANT_Double_info 双精度浮点型字面量 7 CONSTANT_Class_info 类或接口的符号引用 8 CONSTANT_String_info 字符串类型字面量 9 CONSTANT_Fieldref_info 字段的符号引用 10 CONSTANT_Methodref_info 类中方法的符号引用 11 CONSTANT_InterfaceMethodref_info 接口中方法的符号引用 12 CONSTANT_NameAndType_info 字段或方法的部分符号引用 15 CONSTANT_MethodHandle_info 表示方法句柄 16 CONSTANT_MethodType_info 标识方法类型 18 CONSTANT_InvokeDynamic_info 表示一个动态方法调用点
-
常量池里面存放着当前这个类的一些元数据,也就是一些基本资料。例如字面量、符号引用等,其中字面量比较好理解,就是文本字符串或者是常量值等(就是直接量,自身描述自身,"ss"就是表示一个字符串"ss")。符号引用则比较复杂一点,它可以是类和接口的全限定名、字段的名称描述、方法的名称和描述。
三、访问标志
- access_flags
- 类型:u2;访问标志,用于表明当前这个类或者接口的访问信息,例如:它是类还是接口,是不是public的,是不是abstract的,是不是final的等。
四、继承体系描述
- this_class
- 类型:u2;类索引,用于确定这个类的全限定名,u2的数据代表了常量池中一个常量的索引下标,对应的常量就描述了这个类的全限定名。
- super_class
- 类型:u2;父类索引,用于确定父类的全限定名,u2的数据代表了常量池中一个常量的索引下标,对应的常量就描述了这个类的全限定名。
- interfaces_count
- 类型:u2;这个类实现的接口的数量;u2可以表示到65535,也就是类能实现的最大的接口数量。
- interfaces
- 类型:u2;接口索引,用于确定接口的全限定名,u2的数据代表了常量池中一个常量的索引下标,对应的常量就描述了这个接口的全限定名。
五、字段表集合
-
fields_count
- 类型:u2;表示当前类变量或实例变量的数量。
-
field
-
类型:field_info;一种描述当前字段的数据结构,可以包含的信息有,作用域、是否被static修饰、是否final、是否volatile、是否transient、字段的数据类型、字段名称。
名称 描述 类型 access_flag 字段访问标志 u2 name_index 常量池中对应的字段名称的索引 u2 descriptor_index 该字段描述符在常量池中的索引,例如指向常量池中的常量: S,就说明这个字段是 short类型的。 u2 attributes_coun 字段额外属性的数量 u2 attributes 属性表 attrbute_info
-
六、方法表集合
-
method_count
- 类型:u2;类中方法的数量
-
methods
-
类型:method_info;方法表
-
名称 描述 类型 access_flags 方法访问标志 u2 name_index 常量池中对应的方法简单名索引 u2 descriptor_index 方法描述符在常量池中的索引 u2 attributes_count 属性数量 u2 attributes 属性表,预置了多种定义好的属性 attribute_info
-
七、属性表集合
前面的字段表和方法表中都有出现属性表,在class文件、字段表放发表都可以携带自己的属性表集合,用于描述某些常见的专有信息。与其他数据项目不一样,属性表集合的要求很宽松,只要属性名称不重复即可。我们编写的方法,里面的逻辑代码产生的字节码指令就放在方法表的属性表中,属性名字叫做”code“。一个标准的属性应该符合下面的这种结构。
类型 | 名称 | 描述 |
---|---|---|
u2 | attribute_name_index | 属性名称在常量池中的索引 |
u4 | attribute_length | 属性长度,包含了当前两个属性所占长度。 |
u1 | info | 属性体,可以自由发挥 |
以code属性为例
类型 | 名称 | 描述 |
---|---|---|
u2 | attribute_name_index | 同上 |
u4 | attribute_length | 同上 |
u2 | max_stack | 操作数栈,最大深度 |
u2 | max_locals | 存储局部 变量所需空间Slot个数 |
u4 | code_length | 指令条数 |
u1 | code | 指令,u1=256,则指令最多有256种 |
u2 | exception_table_length | |
exception_info | exception_table | |
u2 | attrbute_count | 属性个数 |
attribute_info | attributes | 属性 |