本章节继续学习ClassFile中各项所代表的含义。
常量池
常量池占据了class文件很大一部分数据,里面存储着各式各样的常量信息,包括数字和字符串常量、类和接口名,字段和方法等等,下面我们就来详细了解常量池和各种常量。
ConstantPool结构体
constant_pool.go文件中定义了ConstantPool类型,常量池实际上也是一个表,所以ConstantPool被定义成ConstantInfo的数组。所以,要理解常量池,得先去看ConstantInfo是什么,然后再回过来,此处未完待续,接着往下看。
ConstantInfo
由于常量池中存放的信息各不相同,所以每种常量的格式不同。常量数据的第一个字节时tag,用来区分常量的类型,下面是Java虚拟机规范给出的常量结构:
Java虚拟机一共定义了14中常量,虽然篇幅比较长,但为了看的更清楚,还是列举下吧:
类型 | 标志 | 描述 |
---|---|---|
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_MethodHandle_info | 15 | 表示方法句柄 |
CONSTANT_MothodType_info | 16 | 标志方法类型 |
CONSTANT_InvokeDynamic_info | 18 | 表示一个动态方法调用点 |
ConstantInfo被定义成一个接口,位于constant_info.go文件中,至于为什么ConstantInfo是个接口,看看上面这14种类型就明白了。下面在constant_info.go文件中定义对应tag的常量值,顺便把ConstantInfo接口也贴出来:
继续编辑constant_info.go,添加读取常量信息入口:
由于读取常量信息由具体的常量结构体实现,目前还没有任何实现,所以没办法继续下去了,下面要做的就是去理解并实现14种常量结构。
CONSTANT_Integer_info
CONSTANT_Integer_info使用4个字节存储整数常量,tag占一个字节,一共5个字节。其结构定义如下:
CONSTANT_Integer_info和后面将要介绍的三种数字常量无论结构还是实现都非常相似,所以把它们都定义在cp_numeric.go文件中,ConstantIntegerInfo相关代码如下:
CONSTANT_Integer_info正好可以容纳一个Java的int型常量,但实际上比int更小的boolean、byte、short和char类型的常量也放在CONSTANT_Integer_info。ClassFile.class的int常量class文件如下:
由上图可以看出,tag为0x03,表示CONSTANT_Integer_info。bytes为0x075BCD15,转换成十进制正好是123456789。
CONSTANT_Float_info
CONSTANT_Float_info使用4字节存储IEEE754单精度浮点数常量,tag占一个字节,一共5个字节。其结构和ConstantIntegerInfo非常类似,这里就不给出代码了,在cp_numeric.go文件中可以查看。
CONSTANT_Long_info
CONSTANT_Long_info使用8个字节存储整数常量,tag占一个字节,一共9个字节。其结构和ConstantIntegerInfo非常类似,这里就不给出代码了,在cp_numeric.go文件中可以查看。
CONSTANT_Double_info
CONSTANT_Double_info使用8个字节存储IEEE754双精度浮点数,tag占一个字节,一共9个字节。其结构和ConstantIntegerInfo非常类似,这里就不给出代码了,在cp_numeric.go文件中可以查看。
CONSTANT_Utf8_info
CONSTANT_Utf8_info常量里放的是MUTF-8编码的字符串,结构如下:
要注意的是,字符串在class文件中是以MUTF-8(Modified UTF-8)方式编码的,与正常的UTF-8是有点区别的,具体这里就不讨论了。ConstantUtf8Info位于cp_utf8.go中:
上面代码中decodeMUTF8方法篇幅比较长,是书的作者参照java.io.DataInputStream.readUTF(DataInput)方法写的,这里就不贴出来了,文件中有。像字段名、字段描述符等就是以字符串的形式存储在class文件中的,比如字段PI对应的class文件如下图所示:
上图可以看出,tag为0x01,表示CONSTANT_Utf8_info。length为0x0002,表示bytes的长度为2,所以bytes的值为接下来的后2个字节0x5049,对应的ASCII码正是PI。
下节继续。。。