112. Class-File API: Parsing and Generating Bytecode
The JDK Class-File API provides a standard, evolution-proof library for parsing, generating, and transforming JVM class files, replacing third-party dependencies like ASM and Byte Buddy.
Java 24 (JEP 484 Final)Explanation
For decades, Java frameworks (Spring, Hibernate, Mockito, Quarkus) and toolchains relied on third-party bytecode libraries like ASM, BCEL, or Byte Buddy to read and transform .class bytecode at runtime or build time. However, every time a new Java feature arrived (records, sealed classes, value classes), third-party libraries broke until their maintainers reverse-engineered and updated their parsers for the new class file version format.
JEP 484 (Java 24) introduces the standard Class-File API (java.lang.classfile), designed to evolve lock-step with the JVM specification itself. It follows a clean functional model: - ClassFile.of(): Primary factory for reading, writing, and transforming. - ClassModel: Immutable view of an existing class file. - ClassBuilder: Fluent builder for creating new classes, methods, and attributes. - CodeBuilder: Fluent builder for emitting bytecode instructions (iload, invokevirtual, return).
Because it is built into the JDK, frameworks can generate bytecode safely across upcoming JDK releases without external dependencies.
Example
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
import java.lang.classfile.MethodModel;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
public class ClassFileApiDemo {
public static void main(String[] args) throws Exception {
// 1. Inspect bytecode of an existing class
byte[] bytes = ClassFileApiDemo.class
.getResourceAsStream("ClassFileApiDemo.class")
.readAllBytes();
ClassModel model = ClassFile.of().parse(bytes);
System.out.println("Class Name: " + model.thisClass().asInternalName());
System.out.println("Major Version: " + model.majorVersion());
System.out.println("\nMethods found:");
for (MethodModel method : model.methods()) {
System.out.printf(" - %s %s%n",
method.methodName().stringValue(),
method.methodType().stringValue());
}
// 2. Generate a fresh class file programmatically:
byte[] newClass = ClassFile.of().build(
ClassDesc.of("com.example.GeneratedHello"),
clb -> {
clb.withMethodBody(
"sayHello",
MethodTypeDesc.of(ClassDesc.of("java.lang.String")),
ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
cob -> cob.ldc("Hello from ClassFile API!")
.areturn()
);
}
);
System.out.println("Successfully generated new class: " + newClass.length + " bytes");
}
}Sources
- https://openjdk.org/jeps/484 โ JEP 484: Class-File API (Java 24)
- https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/classfile/package-summary.html โ java.lang.classfile API