Java基础-内部类-为什么局部和匿名内部类只能访问局部final变量
先看下面这段代码:
public class Test {
public static void main(String[] args) { } public void test(final int b) {
final int a = 10;
new Thread(){
public void run() {
System.out.println(a);
System.out.println(b);
};
}.start();
}
}
这段代码会被编译成两个class文件:Test.class和Test1.class。默认情况下,编译器会为匿名内部类和局部内部类起名为Outterx.class(x为正整数)。
根据上图可知,test方法中的匿名内部类的名字被起为 Test$1。
上段代码中,如果把变量a和b前面的任一个final去掉,这段代码都编译不过。我们先考虑这样一个问题:
当test方法执行完毕之后,变量a的生命周期就结束了,而此时Thread对象的生命周期很可能还没有结束,那么在Thread的run方法中继续访问变量a就变成不可能了,但是又要实现这样的效果,怎么办呢?Java采用了 复制 的手段来解决这个问题。将这段代码的字节码反编译可以得到下面的内容:
我们看到在run方法中有一条指令:
bipush 10
这条指令表示将操作数10压栈,表示使用的是一个本地局部变量。这个过程是在编译期间由编译器默认进行,如果这个变量的值在编译期间可以确定,则编译器默认会在匿名内部类(局部内部类)的常量池中添加一个内容相等的字面量或直接将相应的字节码嵌入到执行字节码中。这样一来,匿名内部类使用的变量是另一个局部变量,只不过值和方法中局部变量的值相等,因此和方法中的局部变量完全独立开。
下面再看一个例子:
public class Test {
public static void main(String[] args) { } public void test(final int a) {
new Thread(){
public void run() {
System.out.println(a);
};
}.start();
}
}
反编译得到:
我们看到匿名内部类Test$1的构造器含有两个参数,一个是指向外部类对象的引用,一个是int型变量,很显然,这里是将变量test方法中的形参a以参数的形式传进来对匿名内部类中的拷贝(变量a的拷贝)进行赋值初始化。
也就说如果局部变量的值在编译期间就可以确定,则直接在匿名内部里面创建一个拷贝。如果局部变量的值无法在编译期间确定,则通过构造器传参的方式来对拷贝进行初始化赋值。
从上面可以看出,在run方法中访问的变量a根本就不是test方法中的局部变量a。这样一来就解决了前面所说的 生命周期不一致的问题。但是新的问题又来了,既然在run方法中访问的变量a和test方法中的变量a不是同一个变量,当在run方法中改变变量a的值的话,会出现什么情况?
对,会造成数据不一致性,这样就达不到原本的意图和要求。为了解决这个问题,java编译器就限定必须将变量a限制为final变量,不允许对变量a进行更改(对于引用类型的变量,是不允许指向新的对象),这样数据不一致性的问题就得以解决了。
到这里,想必大家应该清楚为何 方法中的局部变量和形参都必须用final进行限定了。
Java基础-内部类-为什么局部和匿名内部类只能访问局部final变量的更多相关文章
- Java基础内部类、包的声名、访问修饰符、代码块整理
内部类 14.1内部类概念 将类写在其他类的内部,可以写在其他类的成员位置和局部位置,这时写在其他类内部的类就称为内部类.其他类也称为外部类. 内部类分为成员内部类与局部内部类. 我们定义内部类时,就 ...
- 十一、Java基础---------内部类与匿名内部类
内部类分为普通内部类(有名)和匿名内部类.普通内部类分为成员内部类.局部内部类.静态内部类(嵌套内部类).顾名思义,内部类就是定义在一个类内部的类.什么时候都会使用内部类呢?当我们定义一个类,发现内部 ...
- 第30节:Java基础-内部类
内部类 // 外部类 class Demo{ private int num = 3; // 定义内部类 class Int{ void show(){ System.out.println(&quo ...
- “全栈2019”Java第九十七章:在方法中访问局部内部类成员详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- Java 基础 内部类
Java 基础 内部类 内部类(嵌套类) nested class 目的为外围类enclosing class提供服务. 四种: 静态成员类 static member class 非静态成员类 no ...
- Java 匿名内部类 只能访问final变量的原因
文章来源:http://blog.sina.com.cn/s/blog_4b6f8d150100qni2.html 1)从程序设计语言的理论上:局部内部类(即:定义在方法中的内部类),由于本身就是在方 ...
- JAVA基础——内部类详解
JAVA内部类详解 在我的另一篇java三大特性的封装中讲到java内部类的简单概要,这里将详细深入了解java内部类的使用和应用. 我们知道内部类可分为以下几种: 成员内部类 静态内部类 方法内部类 ...
- java基础(十四)-----详解匿名内部类——Java高级开发必须懂的
在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意的事项.匿名内部类使用的形参为何要为final. 使用匿名内部类内部类 匿名内部类由于没有名字,所以它的创建方式有点儿奇怪.创建格式如下: n ...
- Java基础-内部类介绍
java内部类介绍 内部类一共分为4种 成员内部类 静态内部类 方法内部类 匿名内部类 下面我会为大家详细介绍每一个内部类!! 成员内部类 成员内部类就好像是外部类的一个成员属性,也是内部类中最常见的 ...
随机推荐
- Mybatis框架_part1
mybatis介绍 mybatis就是一个封装了jdbc的持久层框架,它的前身是ibatis.Mybatis与hibernate一样都是持久层框架,但是它与hibernate不同的是,它不是一个完全的 ...
- 合工大OJ 1337 一加二减三
Description 题目描述:给一个串,形如一+二-三,求值 Input 第一行为一个正整数T,表示数据的组数,接下来有T行每行都是一个形如一+二-三的串,一,二,三均为正整数 Output 对于 ...
- WWW压缩解压缩
unity的WWW参考文档:http://game.ceeger.com/Script/WWW/WWW.html 在unity中把资源打包成Assetbundle其实把资源通过 LZMA 压缩成二进制 ...
- [3D跑酷] GameManager
GameManager在游戏中很重要,处理整个游戏的流程,但是在这个类中尽量也只是写一些重要的方法,调用其它类中的方法. 枚举项 函数列表 方法解释 //当玩家碰到障碍(障碍Type,碰撞Positi ...
- php strcmp引起的问题
在官方的文档有这么一端说明: Note a difference between 5.2 and 5.3 versions echo (int)strcmp('pending',array()); w ...
- 原生js实现增加(addclass),删除(removeclass),判断是否存在(hasclass),如果存在删除,如果不存在添加(toggleclass)和获取类名(getbyclass)的方法
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- U3D协程Coroutine之WWW与Update()的并行测试
using System.Collections; using UnityEditor; using UnityEngine; using UnityEngine.UI; /************* ...
- php基础02:变量
1.创建变量 <?php $num1 = 15; $num2 = 15.5; echo $num1+$num2; echo "<br>"; ?> 2.Loc ...
- C#报错:创建调试信息文件 ……obj\Debug\model.pdb: 拒绝访问
错误:创建调试信息文件“.......\obj\Debug\model.pdb”时发生错误 --“......\obj\Debug\model.pdb: 拒绝访问. 解决办法如下: 删除该项目下的 b ...
- ultraEdit32 /uedit32 自定义快捷键/自定义注释快捷键
编辑器一直用vim,但同事写VHDL 用的是utraledit32 ,为了更好的沟通,我也下载了最新破解版本:http://pan.baidu.com/s/1qWCYP2W 刚开始用找不到注释的快捷键 ...