- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我想使用ASM在.class文件中添加static final字段,源文件是
public class Example {
public Example(int code) {
this.code = code;
}
public int getCode() {
return code;
}
private final int code;
}
反编译生成的类应该是这样的:
public class Example {
public static final Example FIRST = new Example(1);
public static final Example SECOND = new Example(2);
public Example(int code) {
this.code = code;
}
public int getCode() {
return code;
}
private final int code;
}
作为结论,我想使用 ASM 将 FIRST 和 SECOND 常量添加到 .class 文件,我该怎么做?
最佳答案
这个答案显示了如何使用 ASM 的访问者 api(参见 ASM homepage 上的 ASM 4.0 A Java 字节码工程库 的第 2.2 节),因为它是最熟悉的 api我。 ASM 也有一个对象模型 api(请参阅同一文档中的第二部分)变体,在这种情况下通常更容易使用。对象模型可能有点慢,因为它在内存中构建了整个类文件的树,但如果只有少量类需要转换,性能损失应该可以忽略不计。
创建时static final
值不是常量(如数字)的字段,它们的初始化实际上转到“static initializer block”。因此,您的第二个(转换后的)代码 list 等效于以下 java 代码:
public class Example {
public static final Example FIRST;
public static final Example SECOND;
static {
FIRST = new Example(1);
SECOND = new Example(2);
}
...
}
在一个 java 文件中你可以有多个这样的 static { ... } block ,而在类文件中只能有一个。 java编译器自动将多个静态 block 合并为一个来满足这个需求。当操作字节码时,这意味着如果之前没有静态 block ,那么我们创建一个新的,而如果已经存在一个静态 block ,我们需要将我们的代码添加到现有代码的开头(添加比添加更容易)。
对于ASM,静态 block 看起来像一个具有特殊名称<clinit>
的静态方法。 ,就像构造函数看起来像具有特殊名称的方法一样 <init>
.
在使用visitor api时,要知道一个方法是否已经从之前定义过的方法是监听所有的visitMethod()调用,并检查每个调用中的方法名。访问完所有方法后,将调用 visitEnd() 方法,因此如果到那时还没有访问过任何方法,我们就知道需要创建一个新方法。
假设我们有一个 byte[] 格式的原始类,请求的转换可以这样完成:
import org.objectweb.asm.*;
import static org.objectweb.asm.Opcodes.*;
public static byte[] transform(byte[] origClassData) throws Exception {
ClassReader cr = new ClassReader(origClassData);
final ClassWriter cw = new ClassWriter(cr, Opcodes.ASM4);
// add the static final fields
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "FIRST", "LExample;", null, null).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "SECOND", "LExample;", null, null).visitEnd();
// wrap the ClassWriter with a ClassVisitor that adds the static block to
// initialize the above fields
ClassVisitor cv = new ClassVisitor(ASM4, cw) {
boolean visitedStaticBlock = false;
class StaticBlockMethodVisitor extends MethodVisitor {
StaticBlockMethodVisitor(MethodVisitor mv) {
super(ASM4, mv);
}
public void visitCode() {
super.visitCode();
// here we do what the static block in the java code
// above does i.e. initialize the FIRST and SECOND
// fields
// create first instance
super.visitTypeInsn(NEW, "Example");
super.visitInsn(DUP);
super.visitInsn(ICONST_1); // pass argument 1 to constructor
super.visitMethodInsn(INVOKESPECIAL, "Example", "<init>", "(I)V");
// store it in the field
super.visitFieldInsn(PUTSTATIC, "Example", "FIRST", "LExample;");
// create second instance
super.visitTypeInsn(NEW, "Example");
super.visitInsn(DUP);
super.visitInsn(ICONST_2); // pass argument 2 to constructor
super.visitMethodInsn(INVOKESPECIAL, "Example", "<init>", "(I)V");
super.visitFieldInsn(PUTSTATIC, "Example", "SECOND", "LExample;");
// NOTE: remember not to put a RETURN instruction
// here, since execution should continue
}
public void visitMaxs(int maxStack, int maxLocals) {
// The values 3 and 0 come from the fact that our instance
// creation uses 3 stack slots to construct the instances
// above and 0 local variables.
final int ourMaxStack = 3;
final int ourMaxLocals = 0;
// now, instead of just passing original or our own
// visitMaxs numbers to super, we instead calculate
// the maximum values for both.
super.visitMaxs(Math.max(ourMaxStack, maxStack), Math.max(ourMaxLocals, maxLocals));
}
}
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (cv == null) {
return null;
}
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
if ("<clinit>".equals(name) && !visitedStaticBlock) {
visitedStaticBlock = true;
return new StaticBlockMethodVisitor(mv);
} else {
return mv;
}
}
public void visitEnd() {
// All methods visited. If static block was not
// encountered, add a new one.
if (!visitedStaticBlock) {
// Create an empty static block and let our method
// visitor modify it the same way it modifies an
// existing static block
MethodVisitor mv = super.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
mv = new StaticBlockMethodVisitor(mv);
mv.visitCode();
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
super.visitEnd();
}
};
// feed the original class to the wrapped ClassVisitor
cr.accept(cv, 0);
// produce the modified class
byte[] newClassData = cw.toByteArray();
return newClassData;
}
由于您的问题没有进一步说明您的最终目标到底是什么,我决定使用一个硬编码的基本示例来处理您的 Example 类案例。如果您想创建正在转换的类的实例,则必须更改上面包含“Example”的所有字符串,以使用实际正在转换的类的完整类名。或者,如果您特别想要在每个转换后的类中使用 Example 类的两个实例,则上面的示例按原样工作。
关于java - 如何使用 ASM 为初始值设定项添加静态最终字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11065262/
考虑需要与 iOS 5 和 iOS 6 兼容的应用。 有没有办法标记纯粹为了 iOS 5 兼容性而存在的代码,以便当部署目标最终更改为 iOS 6 时它显示为编译错误(或警告)? 像这样: #IF_D
我想我知道答案但是...有什么方法可以防止全局变量被稍后执行的 修改吗? ?我知道全局变量首先是不好的,但在必要时,有没有办法让它成为“最终”或“不可变”?欢迎黑客/创造性的解决方案。谢谢 最佳答案
class Foo { final val pi = 3 } 是否每Foo对象有一个 pi成员?因此我应该把 pi在伴生对象中? 最佳答案 如果您担心内存占用,您可以考虑将此字段移动到伴随对象中。
随着可用的 Web 开发框架种类繁多,似乎总是有一种“尝试新事物”的永久动机。因此,我们中的一些人发现自己用一个框架换另一个框架,从来没有对最终结果完全满意。当然,总会有一个特定的 Web 框架可以完
在MDN中指出, If the finally block returns a value, this value becomes the return value of the entire try
我正在尝试用 JavaScript 制作一个基本的井字棋类型游戏。尽管 x 和 y 值在 if 语句的范围内,但除最后一个之外的所有空格都有效。 我不知道为什么最后的 else if 语句不起作用。
我想知道如何使用PowerMock模拟kotlin最终类(class),以便进行测试。我按照指南测试了Java最终类,但仍然出现此错误 Cannot subclass final class 有什么办
考虑以下设置: // debugger class public class Debug { // setting public final static boolean DEBUG
给定以下类(class): public class SomeClass { private final int a; public SomeClass(int a) {
This question already has answers here: What does “final” do if you place it before a variable?
我有一个类PasswordEncryptor,它使用org.jasypt.util.password.StrongPasswordEncryptor作为其字段之一,因为我试图使应用程序“可集群”所有类
我今天有一个关于 StreamReader 类的问题。具体使用文件名参数初始化此类例如: TextReader tr = new StreamReader(fileName); 显然,当此操作完成后,
我想弄清楚什么是使用带锁的 try/finally 的最佳方式。 当我在同一个地方有 lock() 和 unlock() 时,我只使用 try/finally block 作为 JavaDoc还建议:
在 Java 中序列化后是否可以将 final transient 字段设置为任何非默认值?我的用例是一个缓存变量——这就是它是 transient 的原因。我还有一个习惯,就是制作不会改变的 Map
在this问题说 final transient 字段在序列化后不能设置为任何非默认值。那么,为什么我为 aVar1 变量设置了 3,为 aVar3 变量设置了 s3? import java.io.
在Xbox上进行开发时,我使用的是F#规范中最终工作流程的修改版。 Xbox上的.net框架似乎不支持尾部调用。因此,我必须在编译时禁用尾部调用优化。 尽管起初看来这种限制会阻止在计算表达式中使用任何
已结束。此问题正在寻求书籍、工具、软件库等的推荐。它不满足Stack Overflow guidelines 。目前不接受答案。 我们不允许提出寻求书籍、工具、软件库等推荐的问题。您可以编辑问题,以便
我想让我的带有自定义对象的ArrayList成为最终对象,以便对象在设置后无法更改。 我试图这样声明它: private final ArrayList XML = new ArrayList();
我有一个场景,我需要类似于 .NET 的 try-catch-finally block 的内容。 在我的尝试中,我将创建一个#temp表,向其中插入数据并基于#temp处理其他数据集。 先是CATC
对此可能有一个简单的答案,但尝试充分使用 Butterknife,将一些 findViewById 转换为 @BindViews,并注意到我无法在需要声明为 Final 的 View 上使用 Bind
我是一名优秀的程序员,十分优秀!