关于 局部内部类 中使用 局部变量 必须声明为 fianl 的理解
这几天刚好看到局部内部类的一些相关知识,对于局部内部类 中使用 局部变量 必须声明为 fianl 的知识点较为困惑,也上网看了好些资料,最后做下总结。
首先从生命周期来看,局部内部类存在于堆空间,和类存在同等级别的生命,只在程序结束或gc回收时结束。
局部变量存在栈空间,随程序消亡而消亡。
那么为什么 局部内部类 中使用 局部变量 必须声明为 fianl 呢?
解释:保持数据的一致性。
基本数据类型:保持值的一致性
引用类型:保持引用的一致性
这个单从普通的局部内部类来解释比较难懂,因此我结合了线程了进行理解。
变量拷贝
首先解释 局部内部类 中使用 局部变量 拷贝 的问题。
先来看这么一个代码段:
public class Test {
public static void main(String[] args) {
final String str = "hapjin";//JDK8 后final可缺省 ,编译器自动补充fianl
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i = 0; i < 10; i++)
System.out.println(str);
}
}).start();
System.out.println("main thread finished");
}
}
这里传递str变量后,str需要在子线程中使用,但是主线程先结束了,那么就会存在问题。假设 str 随着 主线程 先于 子线程 的结束而消亡了(实际也是这样),但是子线程又必须要使用这个已消亡的str变量。那么怎么处理这个使用已消亡的变量问题呢?备份!
对于基本数据类型,备份值。
对于引用数据类型,备份地址值。
因此备份的问题也就可以解释了。
不可更改
那么第二个问题:为什么要声明为fianl?
对于这个解释,做一下假设来反证:
修改上述代码
public class InnerClassUseFinal {
public static void main(String[] args) throws InterruptedException {
String str = "a,b";//JDK8 开始,可以省略fianl,编译器会自动补充
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
String[] split = str.split(",");
System.out.println(split[0]);
}
}).start();
Thread.sleep(45);
//假设代码
//str = null;//假设未声明为fianl,再对变量引用进行修改
System.out.println("main thread finished");
}
}
现在做这样的假设
1.假如没有fianl,即str变量是可更改的,
2.再在主线程中对变量进行了更改
那么子线程的变量操作就觉不可预知性了。代码段只是进行一次更改的情况,假设存在多次修改的情况,那么变得更加不可预测。就上述假设而言,即子线程和主线程在未知的情况下都可能会出现不可预知的执行和未知错误。这样的情况不仅限于线程这一类,可以延展到所有局部内部类。
个人猜测:为了避免这样的程序未知性错误,因此设计者做出了不可更改的的限制。
以上就是个人的一些看法和思考,
具体关于fianl的规定还需要一些深究,但点到为止感觉够用了。
参考资料
匿名内部类访问的局部变量为什么需要用final修饰?
JAVA中内部类(匿名内部类)访问的局部变量为什么要用final修饰?