1、匿名内部类调用实例变量是没有要求的。
2、匿名内部类调用局部变量则要求局部变量必选是final类型的,如果是Lambda表达式则要求局部变量是final或隐式final类型的。
3、实例变量存储在堆中,局部变量存储在栈中;堆在线程之间是共享的,栈则不是,栈是不同的线程互不干涉,所以为了线程安全性都要求使用局部变量。
4、局部变量(存储在栈上)的生命周期是跟着方法的,如果方法调用完毕,那么变量的生命也就结束,如果该方法内部使用了匿名内部类,该内部类又是开启了一个独立的线程,那么他和该方法不在同一个线程上,就会产生异步,可能方法所在的线程比匿名内部类的线程要提前死亡,这个时候方法的局部变量应该也要消亡,然而由于匿名内部类内部调用了方法的局部变量,所以这个时候就会发生错误,因为该变量已经是未定义(undefined)的了。所以,为了解决这个问题,才会硬性的规定 匿名内部类调用的局部对象必须是final类型的,final类型的变量在匿名内部类调用时其实是调用它的副本,由于原本不会发生改变,所以副本也就始终和原本保持一致,就算原本消亡,副本还是可以存在的。
5、联想到JavaScript,JS没有final关键字,而且也没有多线程,所以可以不用这么繁琐,但是有个问题要注意,因为异步将会导致变量的值是不可控的。
6、示例代码
public class InnerClass {
// 实例变量
private String arg = "object-arg";
public void testArgs(){
// 局部变量
String arg2 = "local-arg";
List<String> list = Arrays.asList("a","c","b");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
arg = "inner-arg";
// arg2 ="inner-arg"; 编译错误
return o1.compareTo(o2);
}
});
}
}