Java类文件中的常量池(Constant Pool)是类文件中的一个重要部分,它保存着字面量和符号引用等信息,用于支持代码执行。在Java虚拟机运行时,常量池中的信息会被加载到内存中,并转换为实际的数据类型。常量池是类文件中较为复杂的结构之一,包含多种数据类型和结构。
常量池中包含的数据类型有:
字面量(Literal):如整型、浮点型、字符串等
符号引用(Symbolic Reference):包括类和接口的全限定名、字段的名称和描述符、方法的名称和描述符等
类型描述符(Descriptor):描述字段、方法参数和返回值的数据类型
常量池中的数据结构主要有两种:CONSTANT_xxx_info和CONSTANT_InvokeDynamic_info。前者表示常量池中的常量,包括字面量和符号引用等,后者用于支持Java SE 7中的InvokeDynamic指令。
下面是一个常量池的例子:
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V
#2 = String #16 // Hello, world!
#3 = Fieldref #17.#18 // java/lang/System.out:Ljava/io/PrintStream;
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #21 // HelloWorld
#6 = Class #22 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 HelloWorld.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = Utf8 Hello, world!
#17 = Class #23 // java/lang/System
#18 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
#19 = Class #26 // java/io/PrintStream
#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
#21 = Utf8 HelloWorld
#22 = Utf8 java/lang/Object
#23 = Utf8 java/lang/System
#24 = Utf8 out
#25 = Utf8 Ljava/io/PrintStream;
#26 = Utf8 java/io/PrintStream
#27 = Utf8 println
#28 = Utf8 (Ljava/lang/String;)V
上面的例子是一个简单的HelloWorld程序的常量池,它包含了类中用到的各种字面量、符号引用和类型描述符等。我们可以看到,每个常量都有一个编号,这个编号可以被其他常量引用。常量池的编号从1开始,到常量池大小-1为止”是指常量池中每个项目的编号,而不是指常量池中所有项目的总数。在Java 11及以前的版本中,常量池中的项目数量是16位无符号整数,因此最多可以有65535个项目,编号从1到65534。
在Java 11中,JEP 333 引入了一个新的常量池形式,称为“condy常量池”,这使得常量池中可以包含动态生成的常量,从而提高了JVM的灵活性和性能。由于condy常量池具有不同的结构,因此它的编号方式与普通常量池略有不同。