news 2026/5/1 9:07:42

<span class=“js_title_inner“>【Java 25】Class-File API,解析、生成和转换 Java 字节码的标准 API</span>

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
<span class=“js_title_inner“>【Java 25】Class-File API,解析、生成和转换 Java 字节码的标准 API</span>

Class-File API 是 Java 25 中另外一个实用的功能,它提供了解析、生成和转换 Java 字节码的标准 API。

在 Java 相关的开发中,对字节码(byte code)的操作,一直以来被认为是比较高级的技巧。操作字节码需要第三方库的支持,通常使用的库包括 ASM,ByteBuddy 和 Javassist。其中 ASM 是最老牌的库。JDK 内部都集成了 ASM 的代码。

使用第三方库来操作字节码,存在一些问题。第三方库主要依靠社区维护,功能更新和 bug 修复不够及时。最大的问题在于无法及时提供对最新版本 Java 的支持。在一个 Java 版本发布了之后,需要等待第三方库完成修改,才能应用在新的 Java 版本上。

Java 确实需要一个标准的 API 来对字节码进行操作,这就是 Class-File API。

Class-File API 提供的功能包括字节码的解析、生成和转换。在 API 中,有 3 个不同的部分,分别是 model,builder 和 transform。

model 表示的是模型。根据 Java 字节码的规范,API 中定义了字节码中不同部分的模型。这些模型是一系列的接口。比如,表示类的 ClassModel,表示方法的 MethodModel,和表示字段的 FieldModel 等。解析字节码是把字节码的 byte 数组,转换为 ClassModel。

builder 表示的是构建器,用来构建不同类型的 model。比如构建类的 ClassBuilder, 构建方法的 MethodBuilder,和构建字段的 FieldBuilder 等。这些构建器负责产生所对应的 model。ClassBuilder 构建出来的 ClassModel,可以生成字节码的 byte 数组。

transform 表示的是转换,对一个 model 进行修改。在转换时,进行转换的代码会接收到一个 builder,以及当前的 model。代码可以选择保留,删除或是更新当前的 model,从而完成转换。

下面看一下具体的代码示例。

首先是解析字节码。使用 ClassFile 的 of 方法创建出 ClassFile,再使用 parse 方法解析一个类文件,得到表示该类文件的 ClassModel,最后使用 ClassModel 即可。

在下面的代码中,被解析的是当前的 Java 类的字节码,输出全部方法的名称。

public class ParseClass { void parse() throws IOException { var classModel = ClassFile.of().parse( Path.of("target", "classes", "me", "vividcode", "java21to25", "classfile", "ParseClass.class")); var methods = classModel.methods().stream().map(MethodModel::methodName) .collect( Collectors.joining(", ")); System.out.println(methods); } static void main() throws IOException { new ParseClass().parse(); }}

接着是生成字节码。ClassFile 的 buildTo 方法把生成的字节码输出到文件中。使用 ClassBuilder 构建类,在构建过程中,使用 MethodBuilder 来构建方法。

在下面的代码中,生成了 HelloWorld 类。生成的 HelloWorld 类中,有 greet 和 main 两个方法。在 greet 方法中,使用 IO.println 方法输出 “Hello world”。在 main 方法中,调用 greet 方法。生成的字节码写入到 HelloWorld.class 文件中。

publicclassGenerateClass { voidgenerate() throws IOException { var classDesc = ClassDesc.of("HelloWorld"); ClassFile.of() .buildTo(Path.of("target", "classes", "HelloWorld.class"), classDesc, classBuilder -> { classBuilder.withMethod("greet", MethodTypeDesc.of(CD_void), ACC_STATIC, methodBuilder -> { methodBuilder.withCode(codeBuilder -> { codeBuilder.loadConstant("Hello world"); codeBuilder.invoke(Opcode.INVOKESTATIC, ClassDesc.of("java.lang.IO"), "println", MethodTypeDesc.of(CD_void, CD_Object), false); codeBuilder.return_(); }); }); classBuilder.withMethod("main", MethodTypeDesc.of(CD_void), ACC_PUBLIC | ACC_STATIC, methodBuilder -> { methodBuilder.withCode(codeBuilder -> { codeBuilder.invoke(Opcode.INVOKESTATIC, classDesc, "greet", MethodTypeDesc.of(CD_void), false); codeBuilder.return_(); }); }); }); } staticvoidmain() throws IOException { new GenerateClass().generate(); }}

最后是转换字节码,ClassFile 的 transformClass 方法,使用一个 ClassTransform 对 ClassModel 进行转换。

在下面的代码中,对于方法中的调用指令,检查所调用方法的所有者,是否为 OldService。如果是的话,转换为 NewService。这样就把所有对于 OldService 中方法的调用,自动转换为对 NewService 中对应方法的调用。

public class TransformClass { void transform() throws IOException { CodeTransform codeTransform = (codeBuilder, e) -> { switch (e) { case InvokeInstruction i when i.owner().asInternalName() .contains("OldService") -> codeBuilder.invoke(i.opcode(), ClassDesc.of( "me.vividcode.java21to25.classfile.totransform.NewService"), i.name().stringValue(), i.typeSymbol(), i.isInterface()); default -> codeBuilder.accept(e); } }; var methodTransform = MethodTransform.transformingCode(codeTransform); var classTransform = ClassTransform.transformingMethods( methodTransform); var packagePath = Path.of("target", "classes", "me", "vividcode", "java21to25", "classfile", "totransform"); var classModel = ClassFile.of().parse( packagePath.resolve("ServiceCaller.class")); var bytes = ClassFile.of() .transformClass(classModel, classTransform); Files.write(packagePath.resolve("ServiceCaller.class"), bytes, StandardOpenOption.TRUNCATE_EXISTING); } static void main() throws IOException { new TransformClass().transform(); }}

有了 Class-File API,我们不再需要使用第三方库来操作 Java 的字节码。这样减少了第三方依赖,也提高了代码的可维护性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 6:17:23

<span class=“js_title_inner“>为什么芯片项目需要Makefile?</span>

从RTL到最终流片,中间有几十个工具、上百个步骤。前仿真、后仿真、形式验证、CDC检查、Lint、综合、布局布线、时序分析、功耗分析…每个环节都有一套复杂的命令,每次运行都要敲一长串参数。没有Makefile的项目,就是一座手工作坊。验证阶段&a…

作者头像 李华
网站建设 2026/4/28 11:26:57

上海做肺结节手术的私立医院权威盘点与选择指南

温馨提示:本文内容基于公开医疗信息整理,不作为具体诊疗建议。具体的随访方案或手术安排请咨询专业医生。随着大众健康意识的增强及高分辨率CT筛查的普及,肺结节的检出率逐渐升高。上海作为医疗资源高地,除了实力雄厚的公立三甲医…

作者头像 李华
网站建设 2026/5/1 5:58:50

<span class=“js_title_inner“>.NET 虚拟单体存储库 (VMR)架构演进、同步机制与统一构建策略</span>

摘要本文对.NET 平台的构建架构转型进行了详尽的剖析,特别是从分布式多存储库模式向虚拟单体存储库 (Virtual Monolithic Repository, VMR) 的战略迁移。随着.NET 从 Windows 专有框架演变为跨平台、开源的开发生态系统,其底层的工程复杂性呈指数级增长。…

作者头像 李华