接下来,我们再来看一段代码。
class Hello {
public final String s = "hello";
}
“hello”是一个字符串,它的十六进制为 68 65 6c 6c 6f,我们来看一下它在 class 文件中的位置。
前面还有 3 个字节,第一个字节 0x01 是标识,标识类型为 CONSTANT_Uft8_info,第二个和第三个自己 0x00 0x05 用来表示第三部分字节数组的长度。
与 CONSTANT_Uft8_info 类型对应的,还有一个 CONSTANT_String_info,用来表示字符串对象(之前代码中的 s),标识是 0x08。前者存储了字符串真正的值,后者并不包含字符串的内容,仅仅包含了一个指向常量池中 CONSTANT_Uft8_info 的索引。来看一下它在 class 文件中的位置。
CONSTANT_String_info 通过索引 19 来找到 CONSTANT_Uft8_info。
除此之外,还有 CONSTANT_Class_info,用来表示类和接口,结构和 CONSTANT_String_info 类似,第一个字节是标识,值为 0x07,后面两个字节是常量池索引,指向 CONSTANT_Utf8_info——字符串存储的是类或者接口的全路径限定名。
拿 Hello.java 类来说,它的全路径限定名为 com/itwanger/jvm/Hello,对应的十六进制为“636f6d2f697477616e6765722f6a766d2f48656c6c6f”,是一串 CONSTANT_Uft8_info,指向它的 CONSTANT_Class_info 在 class 文件中的什么位置呢?
先不着急,这里给大家介绍一款可视化字节码的工具 jclasslib bytecode viewer,可以直接在 IDEA 的插件市场安装。安装完成后,选中 class 文件,然后在 View 菜单里找到 Show Bytecode With Jclasslib 子菜单,就可以查看 class 文件的关键信息了。
从上图中可以看到,常量池的总大小为 23,索引为 04 的 CONSTANT_Class_info 指向的是是索引为 21 的 CONSTANT_Uft8_info,值为 com/itwanger/jvm/Hello。21 的十六进制为 0x15,有了这个信息,我们就可以找到 CONSTANT_Class_info 在 class 文件中的位置了。
0x07 是第一个字节,CONSTANT_Class_info 的标识符,然后是两个字节,标识索引。
还有 CONSTANT_NameAndType_info,用来标识字段或方法,标识符为 12,对应的十六进制是 0x0c。后面还有 4 个字节,前两个是字段或者方法的索引,后两个是字段或方法的描述符,也就是字段或者方法的类型。
来看下面这段代码。
class Hello {
public void testMethod(int id, String name) {
}
}
用 jclasslib 可以看到 CONSTANT_NameAndType_info 包含的索引有两个。
一个是 4,一个是 5,可以通过下图来表示 CONSTANT_NameAndType_info 的构成。
对应 class 文件中的位置如下图所示。