为什么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 这样阅读起来会更加舒适一些 非常高兴与大家交流,学习 ...
随机推荐
- python运行报错——注释报错
本人是IT行业的,从事软件测试,还是个菜鸟.希望大神们多多关照~ 首先,开通这个博客的目的: 1)通常我容易犯一些低级的错误,而且在网上找到解决方法,解决之后时间长了又不记得: 2)想和有共同兴趣的人 ...
- cxf整合spring代码
导入jar包cxf的jar包 创建实体类 package com.yhd.webservice.cxf.server.poto; public class Person { private Strin ...
- java面试题 -- 基础
1.抽象和封装的不同点抽象和封装是互补的概念.一方面,抽象关注对象的行为.另一方面,封装关注对象行为的细节.一般是通过隐藏对象内部状态信息做到封装,因此,封装可以看成是用来提供抽象的一种策略.2.重载 ...
- Node.js知识点详解(一)基础部分
转自:http://segmentfault.com/a/1190000000728401 模块 Node.js 提供了exports 和 require 两个对象,其中 exports 是模块公开的 ...
- Mysql主从同步原理简介
1.定义:当master(主)库的数据发生变化的时候,变化会实时的同步到slave(从)库. 2.好处: 1)水平扩展数据库的负载能力. 2)容错,高可用.Failover(失败切换)/High Av ...
- fastdfs+nginx make时报错fatal error:fdfs_define.h: 没有那个文件或目录
环境: ubuntu 18.04.1 fastdfs-nginx-module_v1.16 root@wang-machine:~/桌面/FastDFS# cd nginx-1.8.1/root@wa ...
- CRISPR/Cas9|InParanoid|orthoMCL|PanOCT|pan genome|meta genome|Core gene|CVTree3|
生命组学: 泛基因组学:用于描述一个物种基因组,据细菌基因组动力学,因为细菌的基因漂移使得各个细菌之间的基因组差异很大,(单个细菌之间的基因组差异是以基因为单位的gain&loss,而人类基因 ...
- 对String类型的认识以及编译器优化
Java中String不是基本类型,但是有些时候和基本类型差不多,如String b = "tao" ; 可以对变量直接赋值,而不用 new 一个对象(当然也可以用 new). J ...
- unittest(13)- 从配置文件中读取测试数据
case.config # 1. http_request.py import requests class HttpRequest: def http_request(self, url, meth ...
- 吴裕雄--python学习笔记:BeautifulSoup模块
import re import requests from bs4 import BeautifulSoup req_obj = requests.get('https://www.baidu.co ...