文章参考虚拟机规范Chapter 4. The class File Format,这篇文章只说常量池相关部分。常量池中主要存放两大类常量:字面量(Literal)和 符号引用(Symbolic References)。字面量比较接近于 Java 语言层面的常量概念,如文本字符串、声明为 final 的常量值等。而符号引用则属于编译原理方面的概念,包括了下面三类常量:
- 类和接口的全限定名(Full Qualified Name)
- 字段的名称和描述符(Descriptor)
- 方法的名称和描述符
常量池中的每一项都是一个表,在 JDK1.7 之后共有 14 种结构各不相同的表结构数据,他们都有一个共同点,表开始的第一位是一个 u1 类型的标志位 tag,代表当前这个常量属于哪种常量类型。依旧参考规范
类型 | tag标志 | 描述 |
---|---|---|
CONSTANT_utf8_info | 1 | UTF-8编码的字符串 |
CONSTANT_Integer_info | 3 | 整形字面量 |
CONSTANT_Float_info | 4 | 浮点型字面量 |
CONSTANT_Long_info | 5 | 长整型字面量 |
CONSTANT_Double_info | 6 | 双精度浮点型字面量 |
CONSTANT_Class_info | 7 | 类或接口的符号引用 |
CONSTANT_String_info | 8 | 字符串类型字面量 |
CONSTANT_Fieldref_info | 9 | 字段的符号引用 |
CONSTANT_Methodref_info | 10 | 类中方法的符号引用 |
CONSTANT_InterfaceMethodref_info | 11 | 接口中方法的符号引用 |
CONSTANT_NameAndType_info | 12 | 字段或方法的符号引用 |
CONSTANT_MothodType_info | 16 | 标志方法类型 |
CONSTANT_MethodHandle_info | 15 | 表示方法句柄 |
CONSTANT_InvokeDynamic_info | 18 | 表示一个动态方法调用点 |
描述符本质其实也就是是代表字段或方法类型的字符串,只不过它不会直接存储这个字符串的值,只会指向 CONSTANT_utf8_info 类型的字符串字面量的索引。
举个例子,有下面一个简单的类,查看其字节码
1 | public class Person { |
使用 javac Person.java编译,使用 javap -verbose Person 查看
1 | Classfile /Users/peihuan/workspace/webTest/src/main/java/Person.class |
主要查看的是 Constant pool 这一项, #1 是方法的符号引用,方法符号引用结构如下
1 | CONSTANT_Fieldref_info { |
除了tag还有两个字段,一个指向声明方法的类描述符 CONSTANT_Class_info 的索引项,一个指向名称及类型描述符 CONSTANT_NameAndType 的索引项。#1 指向了 #5 和 #19,我们再看 #5 ,它是 CONSTANT_Class_info ,其结构如下,只有一个指向其全限定名常量项的索引, #5 指向了 #23,就是一个普普通通的字符串了,其值java/lang/Object
就是这个类的名字。
1 | CONSTANT_Class_info { |
再看 #19,这是个 CONSTANT_NameAndType, 其结构如下 ,一个是该方法名称常量项的索引,一个是该方法常量描述符的索引。#19 指向了 #8 和 #9,#8
1 | u1 tag; |
具体的描述符标识字符含义查看表4.2。例如,这个方法 Object m(int i, double d, Thread t) {..}
的描述符就是(IDLjava/lang/Thread;)Ljava/lang/Object;