在JDK1.8之前,如果我们在匿名内部类中访问局部变量,那个这个局部变量必须使用final修饰。而JDK1.8后,我们在匿名内部类中访问局部变量,看似不需要使用final修饰,但是实际上是语法糖(底层还是帮你加上了final)
,即我们在匿名内部类中无法改变该局部变量的引用值。
错误答案:
局部变量如果没有用final修饰,他的生命周期和方法的生命周期是一样的,当方法弹栈,这个局部变量也会消失,那么如果局部内部类对象还没有马上消失想用这个局部变量,就没有了,如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也可以继续使用。
**分析:一个变量加上final难道可以延长生命周期吗?那么岂不是加上final便可以造成短暂的内存泄漏?实际上,传入的内部变量是匿名内部类的成员变量(通过构造函数传入)
。
变量被回收不是因为方法被执行完毕,而是GC Root是否持有对象的引用,事实上变量作为局部内部类构造参数传入,仍然可达,故final并不是延长变量生命周期。
**
正确答案:
加入final修饰为了保持内部和外部数据的一致性~
1. 匿名内部类可以使用的变量
- 外部类的成员变量(所有访问修饰符修饰的变量)。
- 外部方法中的局部变量(需要被final修饰)。
- 外部方法上的参数(需要被final修饰)。
实际上只有第一种变量不需要声明为final。
2. 匿名内部类的构造函数
实际上,匿名内部类和所有类一样,也是有自己的构造函数,只不过这个构造函数是隐式的。
构造函数的参数:
1. 外部对象的引用。(这也是我们能无条件使用外部对象的原因)
2. 匿名内部类使用的局部变量或者方法参数。
1. 反编译前
public class Outer {
String string = ""; //外部类成员变量
void outerTest(final char ch){ //外部方法方法参数
final Integer integer = 1; //外部方法局部变量
Inner inner = new Inner() {
void innerTest() {
//在匿名内部类中均可无条件使用
System.out.println(string);
System.out.println(ch);
System.out.println(integer);
}
};
}
public static void main(String[] args) {
new Outer().outerTest(' ');
}
class Inner {
}
}
2. 反编译后
//匿名内部类
class Outer$1extends Outer.Inner
{
//构造函数,传入的便是外部类引用,以及外部方法的局部变量和方法参数
Outer$1(Outer paramOuter, char paramChar, Integer paramInteger)
{
super(paramOuter);
}
void innerTest()
{
System.out.println(this.this$0.string);
System.out.println(this.val$ch);
System.out.println(this.val$integer);
}
}
3. 使用final修饰的解答
3.1 为什么外部类成员变量不需要final修饰
因为非静态内部类保存了外部类对象的引用,因此内部类对外部类成员变量的修改会真实的反应到外部类实例本身,所以不需要加入final修饰。
3.2 为什么局部变量需要加入final修饰
因为局部变量是匿名内部类的成员变量,即在内部类中对变量的修改也不会影响到外部类的外部方法。
故一刀切,才使用了final修饰。即保证内部类和外部类变量的一致性。