为什么java内部类访问局部变量必须声明为final?
https://blog.csdn.net/z55887/article/details/49229491
先抛出让我疑惑了很久的一个问题
编程时,在线程中使用局部变量时候经常编译器会提示:局部变量必须声明为final
package test;
public class ThreadTest {
public void function(String a) {
new Thread(){
@Override
public void run() {
System.out.println(a);
}
}.start();
}
public static void main(String[] args) {
new ThreadTest().function("a");
}
}
上图中由于方法function中的形参a没有声明为final,编译抛出异常:Cannot refer to the non-final local variable a defined in an enclosing scope
这个问题我特意问过老师,也百度过,都没有给出满意的解答。今天看安卓视频无意发现了答案,真是意外之喜啊!
其实原因就是一个规则:java内部类访问局部变量时局部变量必须声明为final。
那为什么要这样呢?还有线程为什么和内部类一样?接下来我们慢慢揭秘。
public class Out {
public void test(final String a) {
class In{
public void function() {
System.out.println(a);
}
}
new In().function();
}
public static void main(String[] args) {
new Out().test("hi");
}
}
编译这个类后发现产生了两个class文件
也就是说内部类和外部类各一个class文件,这样就产生了一个问题,调用内部类方法的时候如何访问外部类方法中的局部变量呢?
实际上编译后的内部类的构造方法的里面,传了对应的外部类的引用和所有局部变量的形参。
(由于外部类方法执行完后局部变量会消亡,所以内部类构造函数中的局部变量实际是一份“复制”。而为了访问外部类中的私有成员变量,外部类编译后也产生了访问类似与getXXX的方法。)
这时产生了一个不一致的问题,如果局部变量不设为final,那内部类构造完毕后,外部类的局部变量又改变了那怎么办?
public class Out {
public void test(String a) {
class In{
public void function() {
System.out.println(a);
}
}
a="hello";
new In().function();
}
public static void main(String[] args) {
new Out().test("hi");
}
}
如代码中所示,这样调用内部类方法时会造成外部类局部变量和内部类中对应的变量的不一致。(注意内部类编译成class文件与new无关,a="hello"放在new In()前后不影响不一致关系,new在jvm运行class文件时才起效)
理解完内部类必须访问final声明的局部变量原因,我们回到最开始的问题:为什么线程和内部类一样
因为线程也是一个类,所以new Thread也相当于创建了一个内部类啦
我们编译一下最开始的ThreadTest.java文件
发现线程编译后也是产生了单独的class文件。
至此,问题全部解决啦~~
最后说明一下java1.8和之前版本对这个规则编译的区别。
如果在1.8的环境下,会很神奇的发现我们最开始的ThreadTest.java文件编译和运行是完全没有问题的,也就是说内部类使用的局部变量是可以不声明为final?!
且慢,如果我们给局部变量再赋下值会发现编译又会出现同样的错误
public class ThreadTest {
public void function(String a) {
a="b";
new Thread(){
@Override
public void run() {
System.out.println(a);
}
}.start();
}
public static void main(String[] args) {
new ThreadTest().function("a");
}
}
在a="b"这一行报错:Local variable a defined in an enclosing scope must be final or effectively final
也就是说规则没有改变,只是java1.8的编译变得更加智能了而已,在局部变量没有重新赋值的情况下,它默认局部变量为final型,认为你只是忘记加final声明了而已。如果你重新给局部变量改变了值或引用,那就无法默认为final了,所以报错。
参考网站:
详细原理版:java内部类访问局部变量时的final问题 https://blog.csdn.net/xiancaieeee/article/details/8834352
对照原理版:关于java里方法的内部类只能访问被final修饰的局部变量和... http://bbs.itheima.com/thread-136974-1-1.html
jdk不同带来的区别:Java中方法内定义的内部类可以访问方法中的局部变量的问题 https://bbs.csdn.net/topics/390918289?page=1
为什么java内部类访问局部变量必须声明为final?的更多相关文章
- final运用于内部类访问局部变量
final运用于内部类访问局部变量 public void mRun( final String name){ new Runnable() { @Override public void run() ...
- 局部内部类访问它所在方法的局部变量时,要求该局部变量必须声明为final的原因
这是java的一条规则.那么为什么会有这条规则呢?要想弄懂这个问题,就需要弄懂局部内部类对象和局部变量的生命周期的谁更长的问题. 首先,看一段代码,以没有将变量声明为final的代码作为例子,代码如下 ...
- 内部类访问局部变量的时候,为什么变量必须加上final修饰
这里的局部变量就是在类方法中的变量,能访问方法中变量的类当然也是局部内部类了.我们都知道,局部变量在所处的函数执行完之后就释放了,但是内部类对象如果还有引用指向的话它是还存在的.例如下面的代码: cl ...
- 内部类访问局部变量为什么必须要用final修饰
内部类访问局部变量为什么必须要用final修饰 看了大概五六篇博客, 讲的内容都差不多, 讲的内容也都很对, 但我觉得有些跑题了 略叙一下 String s = "hello"; ...
- java-内部类访问特点-私有成员内部类-静态成员内部类-局部内部类访问局部变量
1.内部类访问特点: - 内部类可以直接访问外部类的成员,包括私有. - 外部类要访问内部类的成员,必须创建对象. - 外部类名.内部类名 对象名 = 外部类对象.内部类对象: - 例: class ...
- 匿名/局部内部类访问局部变量时,为什么局部变量必须加final
我们都知道方法中的匿名/局部内部类是能够访问同一个方法中的局部变量的,但是为什么局部变量要加上一个final呢? 首先我们来研究一下变量生命周期的问题,局部变量的生命周期是当该方法被调用时在栈中被创建 ...
- 内部类访问局部变量时,为什么需要加final关键字
是变量的作用域的问题,因为匿名内部类是出现在一个方法的内部的,如果它要访问这个方法的参数或者方法中定义的变量,则这些参数和变量必须被修饰为final.因为虽然匿名内部类在方法的内部,但实际编译的时候, ...
- JAVA中内部类(匿名内部类)访问的局部变量为什么要用final修饰?
本文主要记录:在JAVA中,(局部)内部类访问某个局部变量,为什么这个局部变量一定需要用final 关键字修饰? 首先,什么是局部变量?这里的局部是:在方法里面定义的变量. 因此,内部类能够访问某局部 ...
- 什么是Java内部类?
如果大家想了解更多的知识和技术,大家可以 搜索我的公众号:理想二旬不止 (尾部有二维码)或者访问我的 个人技术博客 www.ideal-20.cn 这样阅读起来会更加舒适一些 非常高兴与大家交流,学习 ...
随机推荐
- JVM简述
JVM的东西太多了,我们刚开始学java的时候,就会接触堆.栈,还有方法区,因为我们要知道new出来的对象放在哪里,局部变量放在哪里,static修饰的变量放在哪里. 我从网上截一个图: 这里有三大部 ...
- yield解析
1.yield可以用来为一个函数返回值塞数据 代码: def addlist(alist): for i in alist: alist = [, , , ] for x in addlist(ali ...
- python代码覆盖率统计-coverage
coverage.py是一个用来统计python程序代码覆盖率的工具.它使用起来非常简单,并且支持最终生成界面友好的html报告.在最新版本中,还提供了分支覆盖的功能. 官方网站: http://ne ...
- CF-558:部分题目总结
题目链接:http://codeforces.com/contest/1163 A .Eating Soup sol:在n / 2.n - m.m三个数中取最小值,结果受这三个值限制.但是m == 0 ...
- sqlserver多表联查分页
select * from(select H_order.Id ,H_order.userID, ROW_NUMBER() over (order by H_order.Id Desc) as row ...
- 古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?(python语言)
# 古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? # 斐波那契数列:0,1,1,2,3,5,8,13, ...
- Python 搭建webdriver环境遇到的问题总结
安装过程是参考<selenium2Python自动化测试实战>中Pythonwebdriver环境搭建章节 在安装过程中,遇到了一些问题,总结一下,为日后自己再遇到相同问题做个笔记以便查看 ...
- Java 关于线程的面试题及答案
一.职场可能碰到的关于线程的面试题: 1. 什么是线程? 线程是程序中一个单一的顺序控制流程.进程内有一个相对独立的.可调度的执行单元,是系统独立调度和分派CPU的基本单位指令运行时的程序的调度单位. ...
- 关于线上bug
之所以想写下线上bug,因为发觉有些公司对线上bug的处理是比较严格甚至是很苛刻,涉及到的相关人可能会因此而背黑锅. 之所以会存在这样情况,因为公司各部门都有关联,特别是用户.老板的投诉,也给公司会造 ...
- vue项目实战
本篇文章主要介绍了vue的环境配置,vue项目的目录结构以及在开发vue项目中问题的一些解决方案. 环境配置及目录结构 1.安装node.js(http://www.runoob.com/nodejs ...