Class文件结构之常量池
在主次版本号之后的是常量池的入口,常量池可以理解为Class文件中的资源仓库,
它是Class文件结构中与其它项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时还是Class中第一个出现的表类型数据项目。
1.常量池存放的内容
- 字面量(Literal):
文本字符串、声明为final的常量值。
- 符号引用(Symbolic References):
类和接口的全限定名(Fully Qualified Name)。
字段的名称和描述符(Descriptor)。
方法的名称和描述符。
这14种常量类型各自均有自己的结构
案例
public class TestConstant {
==
private final int a = 10;
private final int b = 10;
private int c = 11;
private int d = 11;
private long e = -11111110005514L;
private long f = -11111110005514L;
private double g = 10.4557848D;
private double h = 10.4557848D;
private String y = "JVM";
private String j = "JVM";=
}
编译成class文件,使用UltraEdit打开,可以得到Java编译后的字节码
第9,10字节是常量池计数器,表示常量池中有54项常量。(37的十进制值是55)。由于常量池容量计数是从1开始,0用作表示“不引用任何对象”,因此,常量个数为54个。
2.获取第一个常量
第11字节是第一个常量的tag,“0A”对应十进制值是10,根据上表得出的常量类型是CONSTANT_Methodref_info。
CONSTANT_Methodref_info的数据结构
- Tag
表示当前的数据类型CONSTANT_Methodref_info,说明这个常量是一个方法。
- class_index
表示引用的这个方法的对象在常量池数组的索引,说明constant_pool[class_index]存放的就是调用该方法的对象名称。
- Name_and_type_index
表示该方法在常量池数据的索引,即constant_pool[name_and_type_index]存放着该方法的名称。
根据数据结构:“12”对应的十进制值是18,“29”对应的十进制值是41。
那么class_index = 18,name_and_type_index = 41。
得出Class_index[18] = java/lang/Object
Name_and_type_index[41] = "<init>":()V
因此,第一个常量constant_pool[1]为java/lang/Object. "<init>":()V
3.获取第二个常量
第二个常量的tag,“09”对应十进制值是09,根据上表得出的常量类型是CONSTANT_Fieldref_info。
CONSTANT_Fieldref_info的数据结构
- Tag
表示当前的数据类型CONSTANT_Fieldref_info,说明这个常量是一个字段。
- class_index
表示引用的这个方法的对象在常量池数组的索引,说明constant_pool[class_index]存放的就是调用该方法的对象名称。
- Name_and_type_index
表示该方法在常量池数据的索引,即constant_pool[name_and_type_index]存放着该方法的名称。
根据数据结构:“12”对应的十进制值是17,“2A”对应的十进制值是42。
那么class_index = 17,name_and_type_index = 42。
得出Class_index[17] = TestConstant
Name_and_type_index[42] = a:I
因此,第二个常量constant_pool[2]为TestConstant.a:I
依次类推进行寻找常量
4.通过javap对class文件进行解析
当然,我们也可以通过javap对class文件进行解析。
javap命令使用方法和输入方法结果如下:
javap -verbose TestConstant.class
C:\Users\caonanqing\Desktop>javap -verbose TestConstant.class
Classfile /C:/Users/caonanqing/Desktop/TestConstant.class
Last modified 2019-7-22; size 619 bytes
MD5 checksum 5492f3d2a3a963e0a0506757c5fbb22e
Compiled from "TestConstant.java"
public class TestConstant
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #18.#41 // java/lang/Object."<init>":()V
#2 = Fieldref #17.#42 // TestConstant.a:I
#3 = Fieldref #17.#43 // TestConstant.b:I
#4 = Fieldref #17.#44 // TestConstant.c:I
#5 = Fieldref #17.#45 // TestConstant.d:I
#6 = Long -11111110005514l
#8 = Fieldref #17.#46 // TestConstant.e:J
#9 = Fieldref #17.#47 // TestConstant.f:J
#10 = Double 10.4557848d
#12 = Fieldref #17.#48 // TestConstant.g:D
#13 = Fieldref #17.#49 // TestConstant.h:D
#14 = String #50 // JVM
#15 = Fieldref #17.#51 // TestConstant.y:Ljava/lang/String;
#16 = Fieldref #17.#52 // TestConstant.j:Ljava/lang/String;
#17 = Class #53 // TestConstant
#18 = Class #54 // java/lang/Object
#19 = Utf8 a
#20 = Utf8 I
#21 = Utf8 ConstantValue
#22 = Integer 10
#23 = Utf8 b
#24 = Utf8 c
#25 = Utf8 d
#26 = Utf8 e
#27 = Utf8 J
#28 = Utf8 f
#29 = Utf8 g
#30 = Utf8 D
#31 = Utf8 h
#32 = Utf8 y
#33 = Utf8 Ljava/lang/String;
#34 = Utf8 j
#35 = Utf8 <init>
#36 = Utf8 ()V
#37 = Utf8 Code
#38 = Utf8 LineNumberTable
#39 = Utf8 SourceFile
#40 = Utf8 TestConstant.java
#41 = NameAndType #35:#36 // "<init>":()V
#42 = NameAndType #19:#20 // a:I
#43 = NameAndType #23:#20 // b:I
#44 = NameAndType #24:#20 // c:I
#45 = NameAndType #25:#20 // d:I
#46 = NameAndType #26:#27 // e:J
#47 = NameAndType #28:#27 // f:J
#48 = NameAndType #29:#30 // g:D
#49 = NameAndType #31:#30 // h:D
#50 = Utf8 JVM
#51 = NameAndType #32:#33 // y:Ljava/lang/String;
#52 = NameAndType #34:#33 // j:Ljava/lang/String;
#53 = Utf8 TestConstant
#54 = Utf8 java/lang/Object
{
public TestConstant();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 10
7: putfield #2 // Field a:I
10: aload_0
11: bipush 10
13: putfield #3 // Field b:I
16: aload_0
17: bipush 11
19: putfield #4 // Field c:I
22: aload_0
23: bipush 11
25: putfield #5 // Field d:I
28: aload_0
29: ldc2_w #6 // long -11111110005514l
32: putfield #8 // Field e:J
35: aload_0
36: ldc2_w #6 // long -11111110005514l
39: putfield #9 // Field f:J
42: aload_0
43: ldc2_w #10 // double 10.4557848d
46: putfield #12 // Field g:D
49: aload_0
50: ldc2_w #10 // double 10.4557848d
53: putfield #13 // Field h:D
56: aload_0
57: ldc #14 // String JVM
59: putfield #15 // Field y:Ljava/lang/String;
62: aload_0
63: ldc #14 // String JVM
65: putfield #16 // Field j:Ljava/lang/String;
68: return
LineNumberTable:
line 1: 0
line 3: 4
line 4: 10
line 5: 16
line 6: 22
line 7: 28
line 8: 35
line 9: 42
line 10: 49
line 11: 56
line 12: 62
}
SourceFile: "TestConstant.java"
常量池的结束,人工手动找很麻烦,可以直接用javap -verbose TestConstant.class命令找到。
根据得到常量池结束时Object,那么对应字节码得到是在74.