class 文件的常量池里到底保存了什么?这篇用 javap -v 把一个小类的常量池按类型拆开,再用对照实验验证去重、内联和长度限制(invokedynamic 的调用点在后面一篇)。
JDK 21 默认编译:ca fe ba be 00 00 00 41 → 魔数 cafebabe,minor 0,major 65 --release 8 编译:ca fe ba be 00 00 00 34 → major 52 把 major 手工改成 70 再运行(JDK 21 最高支持 65): java.lang.UnsupportedClassVersionError: Demo has been compiled by a more recent version of the Java Runtime (class file version 70.0), this version of the Java Runtime only recognizes class file vers
Utf8 42
NameAndType 11
Class 10
Methodref 7
String 3
MethodHandle 3
InvokeDynamic 2
Integer 1
Long 1
Double 1
Fieldref 1
InterfaceMethodref1
MethodType 1
各类型举例:
#1 Methodref #2.#3 // java/lang/Object."<init>":()V
#2 Class #4 // java/lang/Object
#3 NameAndType #5:#6 // "<init>":()V
#4 Utf8 java/lang/Object
#7 String #8 // hello
#9 Integer 100000
#10 Long 1099511627776l
#12 Double 3.14d
#14 Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream;
#20 InvokeDynamic #0:#21 // #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/Str
#43 InterfaceMethodref#44.#45 // java/lang/Runnable.run:()V
#64 MethodType #6 // ()V
#65 MethodHandle 6:#66 // REF_invokeStatic Demo.lambda$main$0:()V 字面量 "hello" 在源码里写了 3 次:常量池里 String 项 1 个,对应的 Utf8 项 1 个
main 里取常量的指令(节选):
ldc #7 // String hello
ldc #7 // String hello
ldc #7 // String hello
ldc #9 // int 100000
ldc2_w #10 // long 1099511627776l
ldc2_w #12 // double 3.14d
ldc #47 // String lambda
不进常量池的小整数: ['bipush 100', 'bipush 7']第一次(常量都是 1 / v1): User 读到:Const.X = 1,Const.S = v1,Const.y = 1 只重新编译 Const(改成 2 / v2),不重新编译 User,再运行 User: User 读到:Const.X = 1,Const.S = v1,Const.y = 2 User 的字节码里对 Const 的引用: ['3: getstatic #13 // Field Const.y:I'] (只有非 final 的 y 是 getstatic,X 和 S 已经被直接写进了 User)
70000 个字符的字符串字面量:javac 报错 → constant string too long