java为什么匿名内部类的参数引用时final?final局部变量的生命周期

Oriel ·
更新时间:2024-11-10
· 968 次阅读

在知乎上看到了一篇帖子
在这里插入图片描述

在这个问题下面大家已经吵得不可开交了,看了很多篇文章,被误导进了很多的坑,发现只有下面两篇文章是写的最好的,解释的很清楚,我把两篇文章简单的总结了一下:

https://blog.csdn.net/tianjindong0804/article/details/81710268 https://blog.csdn.net/ch717828/article/details/46922777/ 1.问题的引出 public class Hello { public static void main(String[] args) { String str="haha"; new Thread() { @Override public void run() { System.out.println(str); } }.start(); } }

JDK1.8之后做了优化,即不需要将局部变量显示的声明为final也不会发生编译错误,实际上java底层还是加上了final,那么问题来了,为什么匿名内部类访问局部变量要使用final修饰
这个问题的答案就只有一个: 为了保持数据的一致性
对匿名内部类字节码文件进行反编译

public class Hello$1 extends Thread { private String val$str; Hello$1(String paramString) { this.val$str = paramString; } public void run() { System.out.println(this.val$str); } }

也就是说匿名内部类之所以可以访问局部变量,是因为在底层将这个局部变量的值传入到了匿名内部类中,并且以匿名内部类的成员变量的形式存在,这个值的传递过程是通过匿名内部类的构造器完成的。

这里所说的数据一致性,对引用变量来说是引用地址的一致性,对基本类型来说就是值的一致性。

为什么需要用final保护数据的一致性呢?

因为将数据拷贝完成后,如果不用final修饰,则原先的局部变量可以发生变化。这里到了问题的核心了,如果局部变量发生变化后,匿名内部类是不知道的(因为他只是拷贝了局不变量的值,并不是直接使用的局部变量)。这里举个栗子:原先局部变量指向的是对象A,在创建匿名内部类后,匿名内部类中的成员变量也指向A对象。但过了一段时间局部变量的值指向另外一个B对象,但此时匿名内部类中还是指向原先的A对象。那么程序再接着运行下去,可能就会导致程序运行的结果与预期不同。
在这里插入图片描述
可能还有的回答是:

使用final可以保证局部变量的生命周期,因为在方法中,该方法的局部内部类或匿名内部类创建了,对象存在于堆空间,而局部变量则是存在于栈中,随着方法结束变量也就消失,而对象却不一定消失,所以对象引用的就是不存在的变量了,使用final可以在编译期将final送进常量池中

真有这么简单

final修饰的变量确实被当成常量放进了常量池,什么是生命周期?首先变量是在栈空间,对象在堆空间这是没有问题的,栈空间里变量的生命周期就对应着入栈出栈,出栈这个变量就没了,和是不是final有关系?,而对象在堆上的生命周期有专门的GC回收算法

编译器在编译的时候会将这个变量拷贝一份作为匿名内部类的参数继续传递,并且将这个参数赋给当前匿名类的成员变量,用final的目的就是让它不可变,保证数据的统一性,,所以,即使局部变量释放了,那个常量值还存在。
作者:Alphr



匿名内部类 内部类 JAVA final 参数 变量

需要 登录 后方可回复, 如果你还没有账号请 注册新账号