天天看点

一把小刀,直插 class 文件的小心脏(3)

接下来是 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 个。

一把小刀,直插 class 文件的小心脏(3)

来看一个简单的枚举代码。

public enum Color {

   RED,GREEN,BLUE;

通过 jclasslib 可以看到访问标记的信息有 0x4031 [public final enum]。

一把小刀,直插 class 文件的小心脏(3)

对应 class 文件中的位置如下图所示。

一把小刀,直插 class 文件的小心脏(3)

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 文件的小心脏(3)

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 文件中的位置如下图所示。

一把小刀,直插 class 文件的小心脏(3)

07、方法表

方法表和字段表类似,区别是用来存储方法的信息,包括方法名,方法的参数,方法的签名。

就拿 main 方法来说吧。

public class MethodsTest {

先用 jclasslib 看一下大概的信息。

一把小刀,直插 class 文件的小心脏(3)

访问标记是 public static 的。

方法名为 main。

方法的参数为字符串数组;返回类型为 Void。

一把小刀,直插 class 文件的小心脏(3)

继续阅读