点击关注公众号,干货第一时间送达

最近双十一。。。
是的,又到了剁手的时候。
看看所剩无几的余额,只想对当初创造出“双十一”的阿里大佬张勇说,
我谢谢你
话不多说,咱们剩饭继续热着吃。
上篇文章:字节码解析 之 魔数+版本号 主要介绍了字节码文件里面的前两个部分,魔数(Magic Number) 和 版本号 (Version)。
今天,小编来讲解一下跟在版本号后面的另一个部分,常量池(Constant Pool)。
一,常量池(Constant Pool)概述
常量池包含了 class 文件结构及其子结构中所引用的所有字符串常量、类或接口名、字段名和其他常量
演示代码:
public class BytecodeTest {
private int num;
public int add(){
num = num + 2;
return num;
}
}
这里,我们可以借助 jclasslib 工具进行字节码文件查看。
(关于 jclasslib 插件工具的使用,小编在前面的文章:栈帧的内部结构 之 局部变量表 里面有介绍到,感谢的小伙伴可以点击翻看)

这就是常量池(Constant Pool)。
二,常量池中的字面量(Literal)和符合引用(Symbolic Reference)
常量池主要存放两大类常量,字面量(Literal)和符号引用(Symbolic References)。
它包含了 class 文件结构及其子结构中所引用的所有字符串常量、类或接口名、字段名和其他常量.
具体的 字面量 和 符合引用 包括的数据如下:
| 常量 | 具体的常量 |
|---|---|
| 字面量 | 文本字符串 |
| 声明为 final 的常量值 | |
| 符合引用 | 类和接口的全限定名 |
| 字段的名称和描述符 | |
| 方法的名称和描述符 |
1,字面量例子
String str = "pipickeke";
final int age = 18;
2,符合引用的内容概述
(1)全限定名
com/pipickeke/boot/test/BytecodeTest
这种就是 类的全限定名。
(2)简单名称
简单名称指 没有类型和参数修饰符的方法或者字段名称。例如演示代码中的 add() 方法 和 num 字段:
public class BytecodeTest {
private int num;
public int add(){
num = num + 2;
return num;
}
}
简单名称分别是 add 和 num。
(3)描述符
描述符的作用是 用来描述字段的数据类型、方法的参数列表(包括数量、类型和顺序)和返回值。
根据描述符规则,基本数据类型(byte、char、double、float、int、long、short、boolean)以及代表无返回值的 void 类型都用一个大写字符表示,而对象类型则用字符L加对象的全限定名表示。具体如下:
| 标志符 | 含义 |
|---|---|
| B | 基本数据类型byte |
| C | 基本数据类型char |
| D | 基本数据类型double |
| F | 基本数据类型float |
| I | 基本数据类型int |
| J | 基本数据类型long |
| S | 基本数据类型short |
| Z | 基本数据类型boolean |
| V | void类型 |
| L | 对象类型,例如 Ljava/lang/Object; |
| [ | 数组类型,代表一维数组。例如 double[][],对应为 [[D |
看不懂?没关系,来段代码:

懂了吧!
咱们继续。
三,字节码解析 常量池
1,演示代码不变,还是:
public class BytecodeTest {
private int num;
public int add(){
num = num + 2;
return num;
}
}
2,通过 Binary Viewer 查看该源代码,经过编译,生成的字节码文件如下:
(关于 Binary Viewer 软件的使用,小编在之前的文章:字节码解析 之 魔数+版本号 有介绍到,感兴趣的小伙伴可以点击翻看)

一看就懵了吧?
小编第一次看也是完全懵了啊!
不要紧,咱们一点点解析。
3,常量类型和结构参照表
根据Oracle官方提供的数据,制作如下常量池参照表:
(总共有很多种,这里小编只列举了本文演示代码会涉及到的常量)

4,开始解析字节码文件
(1)我们首先把在上面步骤2中,查看得到的字节码文件导入到 Excel
就是下图的数据:

导入到 Excel,如下:

(2)开始解析
1> 魔数(u4:4个字节)、副版本(u2:2个字节)、主版本(u2:2个字节)

可以对照上篇文章:字节码解析 之 魔数+版本号,中的第二步骤的数据表:

2> 常量池计数器(u2:2个字节)

3> 常量池表,表中具体的内容,由前面的 常量池计数器决定,当前有 21 个常量,分别如下:
第1个常量项:(CONSTANT_Methodref_info):

tag 为 0x0A,对应十进制值是 10,参考上面步骤的 常量类型和结构参照表:

第2个常量项:(CONSTANT_Fieldref_info):

tag 为 0x09,对应十进制值是 9,参考上面步骤的 常量类型和结构参照表:

第3个常量项:(CONSTANT_Class_info):

tag 为 0x07,对应十进制值是 7,参考上面步骤的 常量类型和结构参照表:

第4个常量项:(CONSTANT_Class_info):

tag 为 0x07,对应十进制值是 7,参考上面步骤的 常量类型和结构参照表:

第5个常量项:(CONSTANT_utf-8_info):

tag 为 0x01,对应十进制值是 1,参考上面步骤的 常量类型和结构参照表:

图片中,length值为 0x0003,表示该处的字符串长度为 3 ,则后面跟着的 3个字节码值,都属于该常量项,即 0x6E、0x75、0x6D 属于该常量项。
第6 - 17个常量项:(CONSTANT_utf-8_info):
(截图的时候小编数漏了一个
,以文字为准)

第18-19个常量项:(CONSTANT_NameAndType_info):

tag 为 0x0C,对应十进制值是 12,参考上面步骤的 常量类型和结构参照表:

第20-21个常量项:(CONSTANT_utf-8_info):

至此,就是常量池表中全部的 21 个常量项。
我们可以通过 jclasslib 查看字节码文件进行比对:
(关于 jclasslib 插件工具的使用,小编在前面的文章:栈帧的内部结构 之 局部变量表 里面有介绍到,感谢的小伙伴可以点击翻看)

这21个常量项,只对应小编的演示代码,生成的字节码文件。
所以你懂得!
今天的内容就这样。





