JDK 27 里不需要预览开关就能使用的 Class-File API 让 Java 程序可以自己读写 class 文件,不必再依赖 ASM 之类的库。这篇在 JDK 27 上真实解析、生成、转换,并看栈映射与验证。
类 Main$Calc,主版本号 71,访问标志 [PUBLIC, SUPER],方法 4 个,字段 0 个,父类 java/lang/Object 方法 <init> ()V 指令 3 条:[aload_0, invokespecial, return] 方法 sum (II)I 指令 4 条:[iload_1, iload_2, iadd, ireturn] 方法 abs (I)I 指令 7 条:[iload_1, ifge, iload_1, ineg, ireturn, iload_1, ireturn] 方法 hi ()Ljava/lang/String; 指令 2 条:[ldc, areturn] abs 的代码元素种类(含标签、行号):[LineNumber, UnboundLoadInstruction, BoundBranchInstruction, UnboundOperatorInstruction, UnboundReturnInstruction, Label] 方法 abs 的 maxStack = 1,maxLocals = 2
生成的 class:253 字节,魔数 cafebabe,主版本号 71(当前运行时 JDK 27 的版本号 = 71) 加载并调用:twice(21) = 42,sign(-3) = 负,sign(3) = 非负 sign 方法里的 StackMapTable 属性:有,条目 2 个(带分支,API 自动生成) twice(无分支)的 StackMapTable:无(没有分支不需要)
原 class 467 字节 → 转换后 669 字节 进入 sum 调用 sum(2, 3) = 5 进入 abs 调用 abs(-7) = 7 进入 hi 调用静态 hi() = hi 用 ClassTransform.dropping 去掉 abs 之后,方法列表:[hi, sum] ACCEPT_ALL 原样转换:467 字节,与原文件字节相同 = true
JDK 27:java.lang.classfile.ClassFile 可用 = true;包是否需要 --enable-preview:不需要(本次没有加) StackMapsOption.STACK_MAPS_WHEN_REQUIRED:类大小 138 字节,调用 f(5) = 1 StackMapsOption.DROP_STACK_MAPS:VerifyError: Expecting a stackmap frame at branch target 6 故意制造类型错误:返回类型是 String 的方法里写 iconst_1 再 areturn API 生成类型错误的方法:成功,API 没有报错;类大小 123 字节 defineClass:成功(此时还没有链接、没有验证) 第一次使用(链接时验证)→ VerifyError: Bad type on operand stack JDK 21 对比: JDK 21:java.lang.classfile.ClassFile 不存在(ClassNotFoundException) jdk.internal.classfile 存在(内部包)