概述
1.类:
a)数组:
i.创建:
mv.visitInsn(ICONST_3);
mv.visitIntInsn(NEWARRAY, T_INT);
mv.visitVarInsn(ASTORE, 1); // 将数组引用存到局部变量栈1号的位置
等价于:
int[] a = new int[3];
ii.取值:
mv.visitVarInsn(ALOAD, 1); // 数组引用在局部变量栈1号的位置
mv.visitInsn(ICONST_1);
mv.visitInsn(IALOAD);
mv.visitVarInsn(ISTORE, 2);
等价于:
int b = a[1];
iii.赋值:
mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(ICONST_1);
mv.visitInsn(ICONST_2);
mv.visitInsn(IASTORE);
等价于:
a[1] = 2;
b)构造函数:
i. <init>:
1.创建:
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", ()V");
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
说明:构造函数<init>在执行时,需要首先执行父类的构造函数或者类内部其他构造
函数。
2.调用:
mv.visitTypeInsn(NEW, "asm/A");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "asm/A", "<init>", "()V");
mv.visitVarInsn(ASTORE, 1);
等价于:
A a = new A();
说明:在初始化一般对象时,我们需要先调用NEW指令,来创建该对象实例。而由于
后续的INVOKESPECIAL指令是调用类的构造函数,而该指令执行完以后,对对象的引
用将从栈中弹出,所以在NEW指令执行完以后,INVOKESPECIAL指令执行以前,我们
需要调用DUP指令,来增加对象引用的副本。
ii. <clinit>:
1.创建:
MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("hello world");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
mv.visitInsn(RETURN);
mv.visitMaxs(2, 0);
mv.visitEnd();
等价于:
static {
System.out.println("hello world");
}
2. 调用:<clinit>在类被加载时自动调用。
c)字段:
i.一般字段:
1.创建:
FieldVisitor fv = cw.visitField(ACC_PRIVATE, "a", "I", null, null);
fv.visitEnd();
等价于:
private int a;
2.读取:读取类当中名字为a,类型为int的字段的值。
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, "asm/A", "a", "I");
3.设置:
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ICONST_2);
mv.visitFieldInsn(PUTFIELD, "asm/A", "a", "I");
等价于:
a = 2;
ii.静态字段:
1.创建:
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_STATIC, "a", "I", null, null);
fv.visitEnd();
等价于:
private static int a;
2.读取:
mv.visitFieldInsn(GETSTATIC, "asm/A", "a", "I");
3.设置:
mv.visitInsn(ICONST_2);
mv.visitFieldInsn(PUTSTATIC, "asm/A", "a", "I");
等价于:
a = 2;
d)方法:
i. 接口方法:
1.定义:
mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "getA", "()V", null, null);
mv.visitEnd();
2.调用:
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEINTERFACE, "asm/IA", "getA", "()V");
等价于:
public interface IA{
public void geA();
}
public class A implements IA{
public void geA(){…}
}
IA a = new A();
a.getA();
ii.一般方法:
1.定义:
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "getA", "()V", null, null);
mv.visitCode();
mv.visitInsn(RETURN);
mv.visitMaxs(0, 1);
mv.visitEnd();
等价于:
public void getA() {}
2.调用:
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "asm/A", "getA", "()V");
等价于:
A a = new A():
a.getA();
iii.静态方法:
1.定义:
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "getA", "()V", null, null);
mv.visitCode();
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
等价于:
public static void getA() {}
2.调用:
mv.visitMethodInsn(INVOKESTATIC, "asm/A", "getB", "()V");
等价于:
A.getB();
iv.说明:一般方法比静态方法在声明和调用时均要多传入一个this引用作为参数。另外,当使用INVOKESPECIAL来调用方法时,虚拟机将直接根据指令当中所指明的类类型来调用方法;而当使用INVOKEVIRTUAL来调用方法时,虚拟机将根据实例的实际类型来调用方法。
e)异常处理:
i.声明:
mv.visitTryCatchBlock(l0, l1, l1, "java/lang/Exception");
mv.visitLabel(l0);
mv.visitLabel(l1);
…
等价于:
try {
…
} catch (Exception e) {
…
}
说明:在visitTryCatchBlock()当中,第一,二,三个参数均是Label实例,其中一,二表示try块的范围,三则是catch块的开始位置。而第四个参数则是异常类型。而当异常发生时,JVM将会将异常实例置于运行栈的栈顶。
最后
以上就是干净紫菜为你收集整理的asm操作Java(二)的全部内容,希望文章能够帮你解决asm操作Java(二)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复