首先匿名内部类能访问的变量有以下3种:
1.实例变量
2.局部变量
3.外部类的方法参数
需要知道,访问1时不需要final修饰,而访问2、3时需要用final修饰。jdk1.8之后,不需要手动加final修饰符,
编译时底层会自动加上final。
那么为什么要加 final 呢?在这里先了解原因,具体为什么下文会解释。原因:需要保护数据的一致性。
public class OutClass {
String str1 = "str1";//实例变量
//num为外部类的方法参数
public void test(int num) {
String str2 = "str2";//成员变量
Inner inner = new Inner(){
void test1(){
str1 = "s1"; //不会报错
str2 = "s2"; //会报错
num = 0; //会报错
}
};
}
}
1.访问实例变量时不需要 final 修饰的原因:
对程序进行反编译得到如下:
public class OutClass {
String str1 = "str1";
public void test(final int paramInt) {
final String str = "str2";
Inner local1 = new Inner() {
void test1() {
System.out.println(OutClass.this.str1);
System.out.println(str);
System.out.println(paramInt);
}
};
this.str1 = "s1";
}
}
class OutClass$1
extends Inner {
OutClass$1(OutClass paramOutClass, String paramString, int paramInt) {}
void test1() {
System.out.println(this.this$0.str1);
System.out.println(this.val$str2);
System.out.println(this.val$num);
}
}
反编译后发现 str2 和 num 都被加上了 final。
可以看出,匿名内部类访问实例变量 str1 是通过外部类的引用进行的,所以匿名内部类对实例变量 str1 的操作会直接反映到外部。内外数据一致,所以没有必要用 final 限制数据的一致性。
2.访问局部变量需要用 final 修饰的原因:
访问局部变量时,匿名内部类内部复制了一份成员变量 str2 的引用,并保存到匿名内部类自己的成员变量 this.val$str2 中。
假设不需要加 final 修饰 str2,如果匿名内部类中让 val$str2 指向一个新的指向,表面上好像已经成功了,但其实并没有影响到真正的 str2 的指向;再如,改变外部 str2 的指向,但匿名内部类 val&str2 中的指向并没有发生改变,有可能造成运算结果的错误。
既然不加 final 会发生这么多错误,干脆强制性的规定必须加上 final。
3.访问外部类的方法参数需加 final 修饰原因:
访问局部变量需要用 final 修饰的原因同理。