java 内部类的实现原理
一、内部类原理
内部类(inner class) 是定义在类中的类。如下所示:
1 public class Outer{
2 private int num ;
3 public class Inner implements Interface{
4 public void print(){
5 System.out.println(num);
6 }
7 }
8 }
使用内部类有几方面的考虑:
-
方便访问类中的所有数据,包括私有变量、私有方法
-
隐藏对其他类无关的类,比如实现了某个接口的回调类。
-
使用匿名类来简化代码
内部类是一种编译器现象,与虚拟机无关。编译器将内部类翻译成 外部类名$内部类名的常规类文件,JVM层面将其当作普通类文件处理。
使用javap 分析或者使用反射机制对类Outer$Inner进行分析,如下:
1 // javap -p Outer
2 '''外部类文件'''
3 public class Outer{
4 int num;
5 static int access$0(Outer);
6 }
7
8 // javap -p Outer\$1Inner.class
9 '''内部类文件'''
10 public class Outer$Inner{
11 final Outer this$0 ;
12 public Outer$Inner(Outer);
13 public void print();
14
15 }
这里把Outer对象通过构造函数构造进来,就是为了能够访问外围类,且这里是final修饰的,对应唯一。
可以看出内部类持有一个外部类引用,并且有一个隐式的构造器,初始化外部类实例变量。
而编译器在外围类中添加了一个静态方法access$0。为内部类提供外围类num属性的访问,它将返回作为参数的外部类实例的成员变量num。
默认修饰符加static是当前方法只能在同一个包下访问。而内部类正是通过编译器生成一个同一个包下的类,来实现内部类。
内部类中的 System.out.println(num)其实是 System.out.println( Outer.access$0(this$0))
内部类没有什么特殊的,其访问外围类数据的能力都是编译器添加代码实现的。
二、问题
1、匿名内部类为什么只能访问局部的final变量?
其实可以这样想,当方法执行完毕后,局部变量的生命周期就结束了,而局部内部类对象的生命周期可能还没有结束,那么在局部内部类中访问局部变量就不可能了,所以将局部变量改为final,改变其生命周期。
2、匿名内部类为什么访问外部类成员字段不用final?
上面说了,final关键字是为了解决数据不一致的问题,因为内部类中存有外部类的引用,所有对外部类中字段的修改都会真实的反映到外部类实例本身,所以不需要用final来修饰。