要查看 class 的字节码有很多方式,这里我选择自己比较喜欢 vscode 提供插件来查看,下载下图的插件安装后就可以在 vscode 中查看字节码文件。
表示 coffee baby 这是模数,也是校验这个文件是 JVM 认可的字节码文件。如果要是我开发的就修改为 ZI EA 呵呵
魔数
CA FE BA BE
前u4 前四个字节是魔数,表示 coffee baby 这是模数,也是校验这个文件是 JVM 认可的字节码文件。如果要是我开发的就修改为 ZI EA 呵呵
版本信息
00 00 00 34
- 00 00 前两位代表小版本号
- 00 34 表示 52 是 java 版本号 52 对应 java 1.8
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
常量池数量
00 18
24(1 * 16 + 8 = 24) 代表有 23 个常量,为什么是 23 常量而不是 24 常量,因为 0 作为 JVM 保留常量,常量是从 1 开始的,所以需要减去 1 为 24
0A
常量池是以数据表的结构来保存常量信息,这里表是按既定的顺序来组织一类数据。每一个常量根据类型不同而有不同的长度。那么也就是说常量池的长度是可变的。JVM 如何知道读取到哪一位置就结束了常量池呢。首先根据 18(24)进行一条一条地读取常量,每一个常量根据类型可以计算出其长度。这样 JVM 就很容易就可以计算出常量池结束位置。
在开始之前先介绍一下在字节码有关基本类型和引用类型表示,在字节码文件中要尽可能节省空间,所以使用大写字母来表示类型,而且字节码是给机器阅读的。
基本类型 I(int)
这里列出 12 中基本常量,在 JDK 1.7 之后又新增了三种常量,这里不作为重点所以没有罗列出。我们先看一下表,根据结构,常量名称 CONSTANT_utf8_info ,tag 表示常量类型占一个字节,这里的 U1 表示一个字节,然后是长度为 U2 两个字节,先不说了我们通过表来在字节码中读取几个常量就能明白了。
我们使用以上表对应去查找就可以读懂常量池的字节码代表含义了。
接着表示常量数量字节码向下读取
0A
表示 11 对应表中的 CONSTANT_InterfaceMethodref_info 好,找到类型我们继续向下看,第一个两个字节表示引用指向该字段或名称常量项的索引,继续向下两个字节为
00 04
我们可以现在 javap 编译后可读性较高的文件看一下 4 是什么东西
#4 = Class #23 // java/lang/Object
04 表示指向 4 ,其实就是定义构造函数的 <init>
接下来是两个字节是
00 14
14 转为 20 (16 + 4)这里指向一个 20 的在可读 class 文件中内容为两个引用 7 和 8 ,让后将 7 和 8 内容获取进行简单拼接就是 <init>()V
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#20 = NameAndType #7:#8 // "<init>":()V
使用 javap 命令将字节码文件呈现可读文件中对应下面字节码
0A 00 04 00 14
的文本为
#1 = Methodref #4.#20 // java/lang/Object."<init>":()V
1 代表第一行,0A 代表 Methodref 常量的类型,#4 #20 表示两个引用,通过获取两个引用组成字符串为 java/lang/Object."<init>":()V。