ClassReader classReader = new ClassReader(new FileInputStream(new File("input.class")));
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
Files.write(Paths.get("output.class"), classWriter.toByteArray());
反编译
output.class
时,我得到package corrupted_class_files;
input.class
很好,我可以使用ClassReader
来阅读说明,但是我无法保存该类 最佳答案
您的代码缺少将类功能从源实际复制到目标的步骤:
try(FileInputStream in = new FileInputStream(new File("input.class")) {
ClassReader classReader = new ClassReader(in);
ClassWriter classWriter = new ClassWriter(classReader, 0);
classReader.accept(classWriter, 0);
Files.write(Paths.get("output.class"), classWriter.toByteArray());
}
如果您的转换保留了大多数原始类文件,则将
ClassReader
传递给ClassWriter
的构造函数不会复制功能,而是可以进行优化。或者,如the documentation of ClassWriter(ClassReader classReader, int flags)
所述:构造一个新的ClassWriter对象,并为“主要添加”字节码转换启用优化。这些优化如下:
将原始类中的常量池和引导程序方法照原样复制到新类中,这样可以节省时间。如有必要,将在末尾添加新的常量池条目和新的引导程序方法,但是不会删除未使用的常量池条目或引导程序方法。
未转换的方法可以直接从原始类的字节码直接复制到新类中(即,无需为所有方法指令发出访问事件),从而节省了大量时间。 ClassReader接收来自ClassWriter(而不是任何其他ClassVisitor实例)的MethodVisitor对象,可以检测到未转换的方法。
因此,当您在
ClassWriter
方法中将ClassReader
直接链接到accept
时,所有方法访问者都将来自编写器,因此,所有访问者都将直接复制。当您要重大更改类或构造新的类时,应使用构造函数
ClassWriter(int flags)
。请注意,
COMPUTE_FRAMES
已暗含COMPUTE_MAXS
。在上面的示例中,我没有指定任何方法,因为无论如何都复制了方法。当您实际上要更改或添加代码并需要COMPUTE_FRAMES
时,应该为阅读器指定SKIP_FRAMES
,因为无论如何从头开始重新计算原始帧,都没有必要解码。因此,典型的转换设置如下所示:
public class MyClassVisitor extends ClassVisitor {
public MyClassVisitor(ClassVisitor cv) {
super(Opcodes.ASM5, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor visitor = super.visitMethod(
access, name, desc, signature, exceptions);
if(method matches criteria) {
visitor = new MyMethodVisitorAdapter(visitor);
}
return visitor;
}
}
try(FileInputStream in = new FileInputStream(new File("input.class"))) {
ClassReader classReader = new ClassReader(in);
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES);
classReader.accept(new MyClassVisitor(classWriter), ClassReader.SKIP_FRAMES);
Files.write(Paths.get("output.class"), classWriter.toByteArray());
}
在通过构造函数链接访问者时,您没有覆盖的每个方法都会委托给链接的访问者,并在最终目标为
ClassWriter
时复制原始构造。 MethodVisitor
提供的ClassWriter
。如果该方法不满足您的转换条件,则您返回原始的MethodVisitor
,上述优化仍然适用。方法访问者遵循与类访问者相同的模式,覆盖您要拦截的那些方法。顺便说一句,您应该避免混合使用旧的I / O和NIO。您的代码的简化变体如下所示
ClassReader classReader = new ClassReader(Files.readAllBytes(Paths.get("input.class")));
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES);
classReader.accept(new MyClassVisitor(classWriter), ClassReader.SKIP_FRAMES);
Files.write(Paths.get("output.class"), classWriter.toByteArray());
注意阅读和写作之间的对称性
但是,当您使用
getResource
等时,可能会被迫处理InputStream
。但是对于通过系统类加载器可访问的类,您也可以仅将类名称传递给ClassReader(String)
构造函数。