最近学了下aop,并利用asm进行了实践,开发了一个Android的插件(https://github.com/zhoujucheng/bladeaop),使用这个插件的时候发现了一些我不认识的方法,研究了下发现是编译器生成的方法,目的是是解决内部类与外部类的私有字段和私有方法的相互访问的问题。
首先需要知道是内部类是会被编译成一个单独的class文件,从这个角度看的话内部类和普通的类是区别并不大,然而这样的话,外部类是如何能访问到内部类的私有字段和私有方法呢?或者反过来说内部类是如何能访问到外部类的私有字段和私有方法呢?
public class OuterClass {
private InnerClass innerClass = new InnerClass();
public void test(){
System.out.println(innerClass.innerString);
}
private class InnerClass{
private String innerString = "Hello world!";
}
}
可以看到,InnerClass被编译成了OuterClass$InnerClass.class。通过javap -c OuterClass$InnerClass.class得到:
class OuterClass$InnerClass {
final OuterClass this$0;
OuterClass$InnerClass(OuterClass);
Code:
0: aload_0
1: aload_1
2: putfield #2 // Field this$0:LOuterClass;
5: aload_0
6: invokespecial #3 // Method java/lang/Object."<init>":()V
9: aload_0
10: ldc #4 // String Hello world!
12: putfield #1 // Field innerString:Ljava/lang/String;
15: return
static java.lang.String access$000(OuterClass$InnerClass);
Code:
0: aload_0
1: getfield #1 // Field innerString:Ljava/lang/String;
4: areturn
}
从反编译得到的字节码可以看出,多了一个access$000方法,而且是一个static,参数为OuterClass$InnerClass对象,看到这样总算明白了,编译器为我们生成了static方法,然后通过传入内部类的对象访问私有字段和方法。这个内部类像下面这样:
class OuterClass$InnerClass {
private String innerString = "Hello world!";
static String access$000(OuterClass$InnerClass innerClass){
return innerClass.innerString;
}
}
其实内部类访问外部类的私有字段和方法用的也是同一个套路,首先编译器会为内部类生成一个带有外部类参数的构造器,上面的字节码也已经体现出来了,从上面的字节码可以看出,外部类的对象引用会被保存到了内部类的this$0里,当内部类访问外部类的私有字段或方法时,实际上会调用外部类的由编译器生成的static方法,并将内部类的this$0传过去,然后外部类的static方法利用这个参数达到访问私有字段和方法的目的。