--- 常量池及相关内容 --- #### 目录 1. 常量项的类型和关系 2. 信息描述规则 #### 常量项的类型和关系 在 JVM 规范中,常量池的英文叫 Constant Pool,对应的数据结构就是一个类型为 cp_info 的数组,每一个 cp_info 对象存储了一个常量项。cp_info 对应数据结构的伪代码如下: ```java cp_info{ u1 tag; //常量项的类型 u1 info[]; //常量项的内容 } ``` 由伪代码可知,每一个常量项的第一个字节用于表示常量项的类型,紧接其后的才是具体的常量项内容。那么,常量项会有哪些类型呢? | 常量项类型 | tag 取值 | 含义 | | --------------------------- | -------- | ------------------------------------------------------------ | | CONSTANT_Class | 7 | 代表类或接口的信息 | | CONSTANT_Fieldref | 9 | 同下 | | CONSTANT_Methodref | 10 | 这三种常量项有相似的内容,分别存储成员变量、成员函数和接口函数信息。这些信息包括所属类的类名、变量和函数名、函数参数、返回值类型等 | | CONSTANT_InterfaceMethodref | 11 | 同上 | | CONSTANT_String | 8 | 代表一个字符串(String)。注意,该常量项本身不存储字符串的内容,它只存储了一个索引值 | | CONSTANT_Integer | 3 | Java 中,int 和 float 型数据的长度都是 4 个字节。这两种常量项分别代表 int 和 float 型数据的信息 | | CONSTANT_Float | 4 | 同上 | | CONSTANT_Long | 5 | Java 中,long 和 double 型数据的长度都是 8 个字节。这两种常量项分别代表 long 和 double 型数据的信息 | | CONSTANT_Double | 6 | 同上 | | CONSTANT_NameAndType | 12 | 这种类型的常量项用于描述类的成员域或成员函数相关的信息 | | CONSTANT_Utf8 | 1 | 用于存储字符串的常量项。注意,该项真正包含了字符串的内容。而 CONSTANT_String 常量项只存储了一个指向 CONSTANT_Utf8 项的索引 | | CONSTANT_MethodHandle | 15 | 用于描述 MethodHandle 信息。MethodHandle 和反射有关系。Java 类库中对应的类为 java.lang.invoke.MethodHandle | | CONSTANT_MethodType | 16 | 用于描述一个成员函数的信息,只包括函数的参数类型和返回值类型,不包括函数名和所属类的类名 | | CONSTANT_InvokeDynamic | 18 | 用于 invokeDynamic 指令。invokeDynamic 和 Java 平台上实现了一些动态语言(如 Python、Ruby)相类似的有关功能 | ##### CONSTANT_String 和 CONSTANT_Utf8 的区别 CONSTANT_Utf8: 该常量项真正存储了字符串的内容。下面我们将看到此类型常量项对应的数据结构中有一个字节数组,字符串就存储在这个字节数组中。 CONSTANT_String: 代表一个字符串,但是它本身不包含字符串的内容,而仅仅包含一个指向类型为 CONSTANT_Utf8 常量项的索引。 下面我们看看几种常见常量项的内容,如下: ```xml CONSTANT_Utf8_info{ u1 tag; u2 length; u1 bytes[length]; } CONSTANT_Class_info{ u1 tag; u2 name_index; } CONSTANT_Fieldref_info{ u1 tag; u2 class_index; u2 name_and_type_index; } CONSTANT_String_info{ u1 tag; u2 string_index; } CONSTANT_MethodType_info{ u1 tag; u2 descriptor_index; } CONSTANT_Methodref_info{ u1 tag; u2 class_index; u2 name_and_type_index; } CONSTANT_NameAndType_info{ u1 tag; u2 name_index; u2 descriptor_index; } CONSTANT_InterfaceMethodref_info{ u1 tag; u2 class_index; u2 name_and_type_index; } ``` 首先看 CONSTANT_Utf8_info,其中 length 表示 bytes 数组的长度,而 bytes 成员则真正存储字符串的内容。了解这一点信息很重要,因为凡是需要表示字符串的地方实际上都是指向常量池中一个类型为 CONSTANT_Utf8_info 元素的索引。 比如 Class_info 中的 name_index、String_info 中的 string_index、MethodType_info 中的 descriptor_index 等等,都代表一个指向类型为 CONSTANT_Utf8_info 元素的索引。 我们在看下基本数据类型常量项对应的数据结构: ``` CONSTANT_Long_info{ u1 tag; u4 high_bytes; u4 low_bytes; } CONSTANT_Integer_info{ u1 tag; u4 bytes; } CONSTANT_Double_info{ u1 tag; u4 high_bytes; u4 low_bytes; } CONSTANT_Float_info{ u1 tag; u4 bytes; } ``` 我们可以看到以上结构体内直接就能存储数据,这几个 info 之间没有引用关系。 对于前面的 info 而言,不在每个常量项里直接包含字符串信息,而是采用间接引用元素索引的方式,这是**为了节省 Class 文件的空间**,很好理解就不用多说了。 **除了采用引用索引的方式以节省空间外,规范对用于描述成员变量、成员函数相关的字符串的格式也有要求。** #### 信息描述规则 根据 Java 虚拟机规范,如何用字符串来描述成员变量、成员函数是有讲究的,