前置说明:本次讲解的17 种是 JVM 规范定义的「完整标准常量池表类型」 ,无删减无遗漏,所有 Java 版本(JDK8 及以上)完全通用,这 17 种也是
constant_pool常量池数组中cp_info的全部取值类型。
✅ 一、常量池 & cp_info 【前置核心必知】(看不懂这个,后面全白学)
1. 常量池的本质
常量池是 Class 文件中最大、最核心、最复杂 的部分,存储 Class 运行所需的所有字面量 + 所有符号引用,是 JVM 解析类的核心数据源。
2. cp_info 统一结构(所有 17 种类型的共性)
常量池是一个不定长的表结构数组 ,数组中每一个元素都是一个 cp_info 结构,所有 17 种常量类型,都遵循这个统一的结构规范:
bash
cp_info {
u1 tag; // 【类型标记位】1字节,核心!值决定当前常量是哪一种类型
u1 info[]; // 【不定长数据区】跟随tag,不同常量类型,这里的结构完全不同
}
✅ 核心规则:通过第一个字节 tag 的值,就能唯一确定当前常量的具体类型 ,比如 tag=1 → 字符串 UTF-8 常量,tag=7 → 类常量,这是 JVM 解析常量池的核心依据。
3. 常量池的基础规则(3 个高频考点)
① 常量池的索引从 1 开始,索引 0 是保留位,无任何实际意义;
② 常量池的容量 constant_pool_count 是「容量值」,实际元素个数 = constant_pool_count - 1;
③ 17 种类型的tag值是连续无断层的(1~18,仅缺 15),对应刚好 17 种常量类型;
④ 常量池中的常量支持相互引用(最核心的特性),比如:字段引用常量 会引用 类常量 + 名称和类型常量,这是符号引用的核心逻辑。
4. 17 种常量类型【Tag 值速查表】(必备速记,建议收藏)
所有 17 种类型的tag标记值、标准名称一次性列全,按 tag 值升序排列,无任何遗漏,后面的详解完全对应这个列表:
| tag 值 | 常量类型标准名称 | 中文简称 | 核心用途 | 高频度 |
|---|---|---|---|---|
| 1 | CONSTANT_Utf8_info | UTF-8 字符串常量 | 存储所有字符串内容(字面量) | ✅✅✅最高频 |
| 2 | CONSTANT_Integer_info | 整型常量 | 存储 int 类型字面量 | ✅高频 |
| 3 | CONSTANT_Float_info | 浮点型常量 | 存储 float 类型字面量 | ✅高频 |
| 4 | CONSTANT_Long_info | 长整型常量 | 存储 long 类型字面量 | ✅中频 |
| 5 | CONSTANT_Double_info | 双精度浮点常量 | 存储 double 类型字面量 | ✅中频 |
| 6 | CONSTANT_Class_info(旧) | 废弃类型 | JVM 规范保留,无实际用途 | ❌无 |
| 7 | CONSTANT_Class_info | 类 / 接口常量 | 类、接口的符号引用 | ✅✅✅最高频 |
| 8 | CONSTANT_String_info | 字符串常量 | String 字面量的符号引用 | ✅✅✅最高频 |
| 9 | CONSTANT_Fieldref_info | 字段引用常量 | 类的成员变量符号引用 | ✅✅✅最高频 |
| 10 | CONSTANT_Methodref_info | 实例方法引用常量 | 类的实例方法符号引用 | ✅✅✅最高频 |
| 11 | CONSTANT_InterfaceMethodref_info | 接口方法引用常量 | 接口的方法符号引用 | ✅中频 |
| 12 | CONSTANT_NameAndType_info | 名称 + 类型常量 | 字段 / 方法的名称 + 类型描述符 | ✅✅✅最高频 |
| 13 | CONSTANT_MethodHandle_info | 方法句柄常量 | 方法句柄的符号引用 (JDK7+) | ✅低频 |
| 14 | CONSTANT_MethodType_info | 方法类型常量 | 方法的类型描述符 (JDK7+) | ✅低频 |
| 15 | 无 | 无 | 预留 tag 值,未定义类型 | ❌无 |
| 16 | CONSTANT_InvokeDynamic_info | 动态调用点常量 | 动态调用的核心常量 (JDK7+) | ✅中频 |
| 17 | CONSTANT_Dynamic_info | 动态常量 | 动态计算常量的值 (JDK9+) | ✅低频 |
| 18 | CONSTANT_Module_info | 模块常量 | 模块的符号引用 (JDK9+) | ✅低频 |
重要说明:tag=6 的
CONSTANT_Class_info是历史废弃类型,现代 JVM 中完全不用,所以有效标准类型共 17 种,对应 tag:1-5、7-14、16-18。
✅ 二、17 种常量池表类型【分大类 逐类详解】(按重要性排序,重点突出)
为了方便理解和记忆,我不会按 tag 值生硬排序,而是按「用途 + 重要性 」分为 5 大类别 讲解,高频必考的核心类型优先讲、详细讲,低频的新增类型简洁讲,兼顾「应试」和「实战」,这是最高效的学习方式。
✔️ 第一类:基础字面量常量(tag:1/2/3/4/5)【共 5 种,最基础,无引用】
核心特征:存储「直接的字面量数据」,不依赖其他常量,无任何符号引用,是常量池的「原子常量」 ,所有字符串、数值字面量都存在这一类里,tag=1 是重中之重,没有之一。
1. CONSTANT_Utf8_info (tag=1) - 【王者级,最高频,必背】
- 完整结构 :
{u1 tag; u2 length; u1 bytes[length];} - 核心用途 :存储所有的字符串内容 ,包括:类名、接口名、字段名、方法名、字符串字面量、类型描述符(如
Ljava/lang/String;)、注解值等,常量池中所有的文本信息,最终都存储在这个类型中。 - 关键细节 :采用 MUTF-8 编码 (JVM 自定义的 UTF-8 变种),不是标准 UTF-8;
length是字符串的字节数,不是字符数。 - 地位 :常量池的基石 ,90% 的其他常量类型,都会引用这个类型,比如字符串常量、类常量,本质都是指向它的索引。
2. CONSTANT_Integer_info (tag=2)
- 结构:
{u1 tag; u4 bytes;} - 用途:存储int 类型的字面量 (4 字节有符号整数),比如代码中的
int a=100;里的 100。
3. CONSTANT_Float_info (tag=3)
- 结构:
{u1 tag; u4 bytes;} - 用途:存储float 类型的字面量(4 字节单精度浮点)。
4. CONSTANT_Long_info (tag=4)
- 结构:
{u1 tag; u4 high_bytes; u4 low_bytes;} - 用途:存储long 类型的字面量(8 字节有符号长整型)。
- ✅ 高频考点:占 2 个常量池索引位!因为 long 是 8 字节,JVM 规定:该常量的索引位 + 1 会被占用为「无效位」,比如该常量在索引 5,那么索引 6 不可用。
5. CONSTANT_Double_info (tag=5)
- 结构:
{u1 tag; u4 high_bytes; u4 low_bytes;} - 用途:存储double 类型的字面量(8 字节双精度浮点)。
- ✅ 高频考点:和 long 一样,占 2 个常量池索引位,索引 + 1 为无效位。
✔️ 第二类:核心符号引用 - 基础引用型(tag:7/8)【共 2 种,次高频,单级引用】
核心特征:存储「符号引用」,不存储实际数据,只存储「指向其他常量的索引」 ,属于「单级引用常量」------ 只引用【第一类的字面量常量】,是连接字面量和复杂引用的桥梁,同样是必背高频考点。
6. CONSTANT_Class_info (tag=7) - 【高频核心,必背】
- 完整结构:
{u1 tag; u2 name_index;} - 核心用途:存储类 / 接口的符号引用 ,比如:
java/lang/String、com/test/Student这些类的全限定名。 - 关键细节:
name_index是 指向常量池的索引 ,且这个索引一定指向CONSTANT_Utf8_info(tag=1),因为类的全限定名是字符串,存储在 UTF-8 常量中。 - 举例:如果
name_index=10,表示当前类的全限定名,是常量池第 10 个位置的 UTF-8 字符串常量的内容。
7. CONSTANT_String_info (tag=8) - 【高频核心,必背】
- 完整结构:
{u1 tag; u2 string_index;} - 核心用途:存储Java 字符串字面量的符号引用 ,对应代码中的
String s="Java";里的"Java"。 - ✅ 顶级高频误区纠正:该常量不存储字符串内容! 真正的字符串内容在
CONSTANT_Utf8_info中,string_index是指向常量池的索引,且一定指向tag=1的 UTF-8 常量。 - 补充:这就是「字符串常量池」的底层支撑,JVM 运行时会把该常量的引用解析为字符串常量池中的实际对象。
✔️ 第三类:核心符号引用 - 复合引用型(tag:9/10/11/12)【共 4 种,最高频,多级引用】
核心特征:常量池的核心灵魂,存储「字段、方法」的完整符号引用 ,属于「复合引用常量」------ 每个常量都由 2 个索引 组成,分别指向其他常量,形成「多级引用链」,这 4 种是面试最高频考点,必须倒背如流 ,也是 JVM 解析类的字段、方法的核心依据!前置铺垫:CONSTANT_NameAndType_info(tag=12) 是「字段 / 方法的元信息封装」,是这一类的基础,必须先讲它。
8. CONSTANT_NameAndType_info (tag=12) - 【顶级核心,必背】
- 完整结构:
{u1 tag; u2 name_index; u2 descriptor_index;} - 核心用途:存储字段 / 方法的「名称 + 类型描述符」,是字段、方法的核心元信息,所有字段 / 方法引用都依赖它。
- 关键细节:①
name_index:索引,指向CONSTANT_Utf8_info(tag=1)→ 存储字段名 / 方法名(如:age、getName、main);②descriptor_index:索引,指向CONSTANT_Utf8_info(tag=1)→ 存储类型描述符 (JVM 的语法,比如I代表 int,Ljava/lang/String;代表 String,(Ljava/lang/String;)V代表无返回值、参数为 String 的方法)。 - 举例:如果是字段
private int age,则name_index指向 UTF-8 常量"age",descriptor_index指向 UTF-8 常量"I"。
9. CONSTANT_Fieldref_info (tag=9) - 【顶级高频,必背】
- 完整结构:
{u1 tag; u2 class_index; u2 name_and_type_index;} - 核心用途:存储类的成员变量(字段)的完整符号引用 ,包括:实例字段(如
user.name)、静态字段(如Math.PI),所有类的字段,都用这个常量描述。 - 引用规则【必背】:多级引用链,固定不可变✔️
class_index→ 索引,必须指向CONSTANT_Class_info(tag=7)→ 表示这个字段属于哪个类;✔️name_and_type_index→ 索引,必须指向CONSTANT_NameAndType_info(tag=12)→ 表示这个字段的名称 + 类型。 - 引用链总结:
Fieldref → Class → Utf8(类名)+Fieldref → NameAndType → Utf8(字段名) + Utf8(类型)
10. CONSTANT_Methodref_info (tag=10) - 【顶级高频,必背】
- 完整结构:
{u1 tag; u2 class_index; u2 name_and_type_index;} - 核心用途:存储普通类的实例方法的完整符号引用 ,比如
String.substring()、User.getName(),所有类的实例方法,都用这个常量描述。 - 引用规则【和 Fieldref 完全一致,必背】:✔️
class_index→ 指向CONSTANT_Class_info(tag=7)→ 方法所属的类;✔️name_and_type_index→ 指向CONSTANT_NameAndType_info(tag=12)→ 方法的名称 + 参数 + 返回值。 - 注意:不包含静态方法,静态方法也用这个常量(JVM 规范)。
11. CONSTANT_InterfaceMethodref_info (tag=11)
- 完整结构:
{u1 tag; u2 class_index; u2 name_and_type_index;} - 核心用途:存储接口的抽象方法的完整符号引用 ,比如
List.add()、Runnable.run()。 - 引用规则:和上面两个完全一致,唯一区别是
class_index指向的一定是「接口的 Class 常量」。
✔️ 第四类:动态调用相关常量(tag:13/14/16)【共 3 种,JDK7 新增,Lambda 核心】
核心特征:JDK 1.7 为了支持「动态语言调用(invokedynamic 指令)」新增的 3 种常量类型 ,是 Java 8 Lambda 表达式、方法引用、动态代理的底层核心支撑,也是 JVM 的重要新特性,面试中属于「加分项考点」,必须知道用途和来源。
12. CONSTANT_MethodHandle_info (tag=13) - 方法句柄常量
- 结构:
{u1 tag; u1 reference_kind; u2 reference_index;} - 用途:存储方法句柄的符号引用,方法句柄是 JVM 的「方法指针」,可以指向任意方法(普通方法、静态方法、接口方法),是动态调用的核心。
- 核心:
reference_index指向 字段 / 方法引用常量(tag9/10/11)。
13. CONSTANT_MethodType_info (tag=14) - 方法类型常量
- 结构:
{u1 tag; u2 descriptor_index;} - 用途:存储方法的类型描述符 ,本质是对方法的「参数 + 返回值」的封装,
descriptor_index指向 UTF-8 常量(方法的类型描述符字符串)。
14. CONSTANT_InvokeDynamic_info (tag=16) - 动态调用点常量
- 结构:
{u1 tag; u2 bootstrap_method_attr_index; u2 name_and_type_index;} - 核心用途:动态调用的核心常量 ,支撑 JVM 的
invokedynamic指令,是 Java 8 Lambda 表达式、Groovy 等动态语言的底层实现基础。 - 特点:不再依赖固定的类 / 方法引用,而是通过「引导方法」动态计算出调用目标,实现真正的动态绑定。
✔️ 第五类:JDK9+ 新增扩展常量(tag:17/18)+ 废弃类型(tag:6)【共 3 种,低频】
核心说明:这部分是JVM 规范的扩展内容,tag=6 是历史废弃,tag=17/18 是 JDK9 为了支持「模块化系统 (Module)」新增的常量类型,日常开发 / 面试中极少考到,了解即可,不要求背诵,做到「有印象」就行。
15. CONSTANT_Class_info (tag=6) - 废弃常量
- 用途:JVM 规范的历史保留位,早期版本的废弃类型,现代 JVM 中完全不使用,无任何实际意义,仅占一个 tag 值。
16. CONSTANT_Dynamic_info (tag=17) - JDK9 新增,动态常量
- 核心用途:支持动态计算常量的值,可以在运行时通过引导方法动态生成常量,替代部分静态常量,增强灵活性。
17. CONSTANT_Module_info (tag=18) - JDK9 新增,模块常量
- 核心用途:存储Java9 模块化系统 中的「模块名」符号引用,
name_index指向 UTF-8 常量(模块名),是 Java 模块化的底层支撑。
✅ 三、常量池【核心黄金规则】(5 条,吃透 = 掌握常量池本质,必考)
这 5 条规则是常量池的「底层逻辑」,所有 17 种类型的设计、使用都遵循这些规则,面试中但凡问到常量池,一定会围绕这些规则展开,优先级远高于背 17 种类型的结构!
规则 1:【引用为王】常量池的核心是「符号引用」,不是「存储数据」
除了第一类的字面量常量(tag1-5) 存储实际数据,剩下的 12 种全是「符号引用」 ------ 不存储实际内容,只存储「指向其他常量的索引」,JVM 运行时会把这些「符号引用」最终解析为内存中的「直接引用」(内存地址),这是 JVM 类加载的「解析阶段」核心工作。
规则 2:【多级引用链】复合常量的引用是「链式的、固定的」
所有复合引用常量(tag9/10/11)的引用关系是JVM 规范强制规定 的,不可改变,比如:CONSTANT_Fieldref_info → 必须指向 CONSTANT_Class_info + CONSTANT_NameAndType_info``CONSTANT_NameAndType_info → 必须指向 CONSTANT_Utf8_info + CONSTANT_Utf8_info这种「链式引用」最终都会溯源到 tag=1 的 UTF8 常量,这是常量池的「数据闭环」。
规则 3:【索引从 1 开始】永远的铁律,高频考点
常量池的索引绝对不会从 0 开始 ,索引 0 是「保留位」,表示「无引用」,比如:类的父类索引为 0,表示这个类是java/lang/Object(无父类)。
规则 4:【long/double 占两位】唯一的例外,必考
tag4 (long)、tag5 (double) 是常量池中唯一占用 2 个索引位的类型,因为它们是 8 字节数据,JVM 规定:这两个常量的「当前索引 + 1」会被标记为无效位,不可用。
举例:常量池索引 5 是 long 常量,那么索引 6 一定是无效位,下一个可用索引是 7。
规则 5:【UTF8 是基石】所有文本信息最终都在 tag1
CONSTANT_Utf8_info(tag=1) 是常量池的「基石中的基石」,没有它,其他所有常量都无法工作!所有的类名、方法名、字段名、字符串内容、类型描述符,最终都存储在这个类型中,其他常量只是指向它的索引。
✅ 四、高频必考「核心常量总结」
✅ 必背 8 种(按重要性排序):
- CONSTANT_Utf8_info (tag=1) - 所有字符串的存储地
- CONSTANT_Class_info (tag=7) - 类 / 接口的符号引用
- CONSTANT_NameAndType_info (tag=12) - 字段 / 方法的名称 + 类型
- CONSTANT_Fieldref_info (tag=9) - 字段的完整引用
- CONSTANT_Methodref_info (tag=10) - 方法的完整引用
- CONSTANT_String_info (tag=8) - 字符串字面量引用
- CONSTANT_Integer_info (tag=2) - 整型字面量
- CONSTANT_Float_info (tag=3) - 浮点型字面量
✅ 五、实操查看:如何看真实的常量池内容?(一行命令搞定)
理论看完,必须落地实操,用 JDK 自带的工具就能查看任意 Class 文件的常量池,看到真实的 17 种类型(实际中大部分是高频的 8 种),命令如下:
bash
# 核心命令:反编译Class文件,查看完整的常量池+所有结构
javap -v 你的类名.class
示例:
javap -v Student.class执行后,控制台会输出Constant pool:部分,里面就是该类的常量池,每行都是一个常量,格式如下:
bash
Constant pool:
#1 = Utf8 com/test/Student
#2 = Class #1 // com/test/Student
#3 = Utf8 java/lang/Object
#4 = Class #3 // java/lang/Object
#5 = Utf8 name
#6 = Utf8 Ljava/lang/String;
#7 = NameAndType #5:#6 // name:Ljava/lang/String;
#8 = Fieldref #2:#7 // com/test/Student.name:Ljava/lang/String;
✅ 对应关系:#数字 是常量池的索引,后面的Utf8/Class/Fieldref就是我们讲的 17 种类型,完美对应!
✅ 最终总结
- JVM 常量池的
cp_info共17 种标准表类型 ,由tag值唯一标识,tag 范围:1-5、7-14、16-18; - 17 种类型分 5 大类:字面量常量 (5)、基础引用常量 (2)、复合引用常量 (4)、动态调用常量 (3)、扩展常量 (3);
- 常量池的核心是「符号引用」,除了字面量,其余都是指向其他常量的索引,形成多级引用链;
- CONSTANT_Utf8_info(tag=1) 是基石,所有文本信息都存在这里;
- 高频必考 8 种类型,吃透即可应对所有面试 / 开发场景;
- 常量池索引从 1 开始,long/double 占 2 个索引位,是两大铁律。