接下来是 CONSTANT_Fieldref_info 、CONSTANT_Methodref_info 和 CONSTANT_InterfaceMethodref_info,它们三个的结构比较类似,可以通过下面的伪代码来表示。
CONSTANT_*ref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
学过 C 语言的符号表(Symbol Table)的话,对这段伪代码并不会陌生。
tag 为标识符,Fieldref 的为 9,也就是十六进制的 0x09;Methodref 的为 10,也就是十六进制的 0x0a;InterfaceMethodref 的为 11, 也就是十六进制的 0x0b。
class_index 为 CONSTANT_Class_info 的常量池索引,表示字段 | 方法 | 接口方法所在的类信息。
name_and_type_index 为 CONSTANT_NameAndType_info 的常量池索引,拿 Fieldref 来说,表示字段名和字段类型;拿 Methodref 来说,表示方法名、方法的参数和返回值类型;拿 InterfaceMethodref 来说,表示接口方法名、接口方法的参数和返回值类型。
还有 CONSTANT_MethodHandle_info 、CONSTANT_MethodType_info 和 CONSTANT_InvokeDynamic_info,我就不再一一说明了,大家也可以拿把小刀去试一试。
啊,class 文件中最复杂的常量池部分就算是解剖完了,真不容易!
04、访问标记
紧跟着常量池之后的区域就是访问标记(Access flags),这个标记用于识别类或接口的访问信息,比如说到底是 class 还是 interface?是 public 吗?是 abstract 抽象类吗?是 final 类吗?等等。总共有 16 个标记位可供使用,但常用的只有其中 7 个。
来看一个简单的枚举代码。
public enum Color {
RED,GREEN,BLUE;
}
通过 jclasslib 可以看到访问标记的信息有 0x4031 [public final enum]。
对应 class 文件中的位置如下图所示。
05、this_class、super_class、interfaces
这三部分用来确定类的继承关系,this_class 为当前类的索引,super_class 为父类的索引,interfaces 为接口。
来看下面这段简单的代码,没有接口,默认继承 Object 类。
class Hello {
public static void main(String[] args) {
}
}
通过 jclasslib 可以看到类的继承关系。
this_class 指向常量池中索引为 2 的 CONSTANT_Class_info。
super_class 指向常量池中索引为 3 的 CONSTANT_Class_info。
由于没有接口,所以 interfaces 的信息为空。
对应 class 文件中的位置如下图所示。
06、字段表
一个类中定义的字段会被存储在字段表(fields)中,包括静态的和非静态的。
来看这样一段代码。
public class FieldsTest {
private String name;
}
字段只有一个,修饰符为 private,类型为 String,字段名为 name。可以用下面的伪代码来表示 field 的结构。
field_info {
u2 access_flag;
u2 name_index;
u2 description_index;
}
access_flag 为字段的访问标记,比如说是不是 public | private | protected,是不是 static,是不是 final 等。
name_index 为字段名的索引,指向常量池中的 CONSTANT_Utf8_info, 比如说上例中的值就为 name。
description_index 为字段的描述类型索引,也指向常量池中的 CONSTANT_Utf8_info,针对不同的数据类型,会有不同规则的描述信息。
1)对于基本数据类型来说,使用一个字符来表示,比如说 I 对应的是 int,B 对应的是 byte。
2)对于引用数据类型来说,使用 L***; 的方式来表示,L 开头,; 结束,比如字符串类型为 Ljava/lang/String;。
3)对于数组来说,会用一个前置的 [ 来表示,比如说字符串数组为 [Ljava/lang/String;。
对应到 class 文件中的位置如下图所示。
07、方法表
方法表和字段表类似,区别是用来存储方法的信息,包括方法名,方法的参数,方法的签名。
就拿 main 方法来说吧。
public class MethodsTest {
public static void main(String[] args) {
}
}
先用 jclasslib 看一下大概的信息。
访问标记是 public static 的。
方法名为 main。
方法的参数为字符串数组;返回类型为 Void。
对应到 class 文件中的位置如下图所示。