灵魂拷问:Java内部类是如何访问外部类私有对象的?
共 2831字,需浏览 6分钟
·
2021-03-12 18:52
点击上方 好好学java ,选择 星标 公众号
重磅资讯,干货,第一时间送达 今日推荐:14 个 github 项目!
个人原创100W +访问量博客:点击前往,查看更多
我们都是知道内部类就是写在一个类里面的类,类里面的类,不知道你们有没有看过javac之后的结果,就是将一个外部类编译后,生成的字节码文件结果
public class OutClass{
private String name;
private int id;
private String address;
public class innerClass{
private String innerName;
public void fun(){
System.out.println(OutClass.this.name+": "+innerName);
System.out.println(OutClass.this.id+": "+innerName);
System.out.println(OutClass.this.address+": "+innerName);
}
public innerClass(String innerName){
this.innerName = innerName;
}
}
}
这是我简单手写的一个外部类中嵌套一个内部类。
当我编译这段代码javac OutClass.java
可以看出会生成两个.class字节码文件,内部类的类名是外部类类名$内部类类名
然后对这个两个字节码文件反编译看看javap
可以看到,外部类OutClass除了默认构造器和私有的属性:name,id,address还多了三个静态的方法,这三个方法不是我们手写的。是编译器自动生成的,什么作用呢。
不晓得๑乛◡乛๑,然后看内部类OutClass$innerClass
,发现编译器也做了修改,首先,多了一个常量引用,final OutClass this$0
很明显,这就是指向外部类的引用,有了引用怎么对他赋值呢,然后我们看到了那个构造方法,我自己的源代码中构造方法的参数只有一个String innerName 而通过反编译我看到了多了一个参数,一个类型为OutClass,这就很明显了嘛。
编译器小哥偷偷的做了一些不可告人的事情,首先,内部类中多了个常量引用,准备指向着外部类,而且又偷偷修改了构造方法。传递一个OutClass类型的参数进去。这样内部类就拿到了外部类的引用。
但是仅仅拿到引用有个毛线用,通过反编译可以看到,生成的是两个字节码文件,在虚拟机看来,这就是两个不相关的类,你能在一个类中调用另外一个类的私有属性吗???
很明显不能。这个时候我做了个方法的测试呀,我们都知道,内部类使用外部类的属性用过外部类类名.this.属性名,所以我写了个测试方法fun
public void fun(){
System.out.println(OutClass.this.name+": "+innerName);
System.out.println(OutClass.this.id+": "+innerName);
System.out.println(OutClass.this.address+": "+innerName);
}
然后我们通过反编译看看这段代码怎么执行的。嘿嘿反编译真是个好东西(●´∀`●)4
javap -c OutClass$innerClass
看上去看不懂有点复杂,但是没关系我们看右边,看我红色箭头,是不是有点属性,
Field this$0:LOutClass;
Method OutClass.access$000:(LOutClass;)Ljava/lang/String;
截取一部分,看见没有,上面那个属性是内部类自动生成的常量指针,下面那个方法是外部类自动生成的三个静态方法。将指向外部类的引用作为参数给那三个外部类中的静态方法
然后我们去反编译看看那三个静态方法怎么实现的
又是祭出伟大的反编译工具
看得出,这三个方法都是返回外部类对应的私有属性!不过对于这点我还有点要说明,编译器很智能,它会扫描内部类,查看是否调用的外部类的私有属性,只有调用了才会生成对应的acess$xxx方法!
结论
在虚拟机中没有外部类内部类之分都是普通的类,但是编译器会偷偷的做点修改,让内部类中多一个常量引用指向外部类,自动修改内部类构造器,初始化这个常量引用,而外部类通过扫描内部类调用了外部类的那些私有属性,为这些私有属性创造acess$xxx静态方法。这个方法是返回对应的私有属性的值。所以可以在一个类的外部获取一个类的私有属性的值
更多项目源码