Java内部类访问局部变量时的final问题
2019独角兽企业重金招聘Python工程师标准>>>
JAVA用了也快三年了,内部类访问局部变量的情况也没少遇到。也一直知道要给变量加个final修饰符,不然通过不了编译。但一直也没深究过为什么要加。昨天好奇的上网查了下,并翻阅了下相关的书籍(Core Java 8th),终于算是搞明白了,在这里简单说明下。
说先我们来看一段示例代码:
01 public void start(int interval,final boolean beep) {
02
03 // Inner Class
04 class TimePrinter implements ActionListener {
05
06 @Override
07 public void actionPerformed(ActionEvent event) {
08 Date now = new Date();
09 System.out.println(“At the tone, the time is “ + now);
10 if (beep) {
11 Toolkit.getDefaultToolkit().beep();
12 }
13 }
14 }
15 //
16 ActionListener listener = new TimePrinter();
17 Timer t = new Timer(interval, listener);
18 t.start();
19 }
我们在start函数中定义了一个内部类TimePrinter,其中访问了函数的局部变量beep。
为了说明内部类访问局部变量为什么要加final关键字,我们先来看一下JAVA对内部类的实现。
假设上述代码中start函数所在的类的名称为TalkingClock。则编译上述代码的时候,JAVA编译器会把TimePrinter内部类编译为一个独立的class文件。其形式如下(类名为:外部类$内部类):
01 class TalkingClock$1TimePrinter {
02 // 添加的构造函数,参数为外部类对象的引用和该内部类访问的局部变量的引用(这里为boolean类型)
03 TalkingClock$1TimePrinter(TalkingClock, boolean);
04 // 内部类原有的函数
05 public void actionPerformed(java.awt.event.ActionEvent);
06 // 局部变量的引用
07 final boolean val$beep;
08 // 外部类对象的引用
09 final TalkingClock this$0;
10 }
通过上述类的定义,我们可以看出内部类在构造的时候,会被编译器自动传入外部类对象的一个引用,同时也会传入内部类访问的局部变量的引用,这也就解释了内部类对象为什么可以访问外部类的成员变量和函数还有局部变量了。但是由于这些工作是在编译时进行的,JAVA虚拟机并没有什么所谓的内部类的概念,在JAVA虚拟机看来,该内部类和外部类是两个独立的class文件。我们知道,一个类的私有函数和成员变量是不能被其他类访问的。那么内部类又是如何访问外部类的私有成员变量和函数呢?
我们假设外部类中有一个私有的int型的变量counter。我们想在内部类中访问它。其实在编译的时候,为了内部类可以访问外部类的私有变量,JAVA 编译器还偷偷做了一些额外的工作。编译器除了上文提到的会生成一个内部类类,同时还会修改我们的外部类代码。其修改如下:
1 class TalkingClock {
2 // 编译器自动添加的函数,用来访问私有成员变量counter
3 static int access$0(TalkingClock);
4 // 原有的函数
5 public void start();
6 // 私有成员变量
7 private int counter
8 }
可以看出,为了访问counter私有成员变量,编译器偷偷的为我们添加了一个access$0的静态函数,它接收一个TalkingClock对象的引用,并返回该对象内的coutner的值。并且编译器会把我们在内部类中用到counter的地方都替换为TalkingClock.access$0(this$0)。
好了,现在我们已经了解了内部类的实现机制,那我们最后来看一下访问局部变量为什么要求局部变量添加final关键字。
还是以我们最开始的那个start函数为例。我们来看一下该函数的可能的执行过程:
如果beep变量不被标注为final,那么就意味着我们可以随时修改beep的值。假设我们在创建了TimePrinter对象后修改了beep的值,那么这时我们的内部类所看到的beep的值还是之前通过构造函数传递进去的老值,这样就导致内部类和外部函数对beep值“认识”的不一致。所以final关键字的目的就是为了保证内部类和外部函数对变量“认识”的一致性。
结束语:这个内部类final的问题从最开始学JAVA时就遇到了,期间也有想过为什么要加,但最终都没有深究,就理所当然的认为是规定了。其实这是一个很不好的习惯。学习东西就要“知其然,知其所以然”,一知半解最是害人。希望大家都可以引以为鉴。
转载于:https://my.oschina.net/u/1024767/blog/467109
Java内部类访问局部变量时的final问题相关推荐
- 第三次学JAVA再学不好就吃翔(part43)--局部内部类访问局部变量
学习笔记,仅供参考 面向对象 局部内部类访问局部变量 啥叫局部内部类? 局部内部类是指在方法中定义的内部类. 举个例子(访问局部内部类) 局部内部类: class InnerTest1 {public ...
- 内部类访问局部变量的时候,为什么变量必须加上final修饰
这里的局部变量就是在类方法中的变量,能访问方法中变量的类当然也是局部内部类了. 我们都知道,局部变量在所处的函数执行完之后就释放了,但是内部类对象如果还有引用指向的话它是还存在的.例如下面的代码: c ...
- java实例变量,局部变量,类变量和final变量
题目 答案:B 解析 实例变量: 定义在类中的变量是类的成员变量,可以不进行初始化, java 会自动进行初始化.(如果是引用类默认初始化为 null, 如果是基本类型,默认初始化为 0 ) 局部变量 ...
- c++局部对象是什么_什么是Java内部类?
内部类 (一) 概述 把类定义在另一个类的内部,该类就被称为内部类. 举例:把类Inner定义在类Outer中,类Inner就被称为内部类. class Outer {class Inner {}} ...
- java 内部类 返回值_Java基础第10天+形式参数和返回值的问题、包、导包、权限修饰符、常见的修饰符、内部类(局部内部类,成员内部类,匿名内部类)...
1:形式参数和返回值的问题(理解) (1)形式参数: 类名:需要该类的对象 抽象类名:需要该类的子类对象 接口名:需要该接口的实现类对象 (2)返回值类型: 类名:返回的是该类的对象 抽象类名:返回的 ...
- 10-05 Java 内部类概述和讲解
内部类的概述 /*内部类概述: 把类定义在其他类的内部,这个类就被称为内部类.举例:在类A中定义了一个类B,类B就是内部类.内部的访问特点:A:内部类可以直接访问外部类的成员,包括私有.B:外部类要访 ...
- 局部内部类访问方法中的局部变量为什么加final
1)从程序设计语言的理论上:局部内部类(即:定义在方法中的内部类),由于本身就是在方法内部(可出现在形式参数定义处或者方法体处),因而访问方法中的局部变量(形式参数或局部变量)是天经地义的.是很自然的 ...
- Java内部类详解(使用场景和好处、相关内部类的笔试面试题)
此篇文章作者为:Matrix海子 出处:http://www.cnblogs.com/dolphin0520/ 最近也是在学习java,看的是<java核心技术卷一>jdk8,看到内部类的 ...
- java内部类写法_Java学习-内部类
一.概念 内部类:在一个类的内部定义的一个类: 分类: 可以分为 成员内部类.局部内部类.匿名内部类和静态内部类: <Think in java>): 1.内部类可以用多个实例,每个实例都 ...
- java内部类的本质
连接与通信,作为桥接中间件存在. 内部类和主体类可以无障碍通信: 1.通过继承连接实现: 2.通过接口连接通信: 形式: 1.命名空间: 2.运行上下文: 其它: 信息隐藏是次要功能. 内部类 Jav ...
最新文章
- 网络推广外包专员浅析如何在网络推广外包中获得相关关键词?
- SQL SERVER DBCC 命令集整理
- netty:NIO模型--选择器(Selector)
- html3D效果可以在手机打开吗,手机怎么打开HTML
- linux注意的一些地方
- php word com,php 展示word
- 5,线程池,进程池,协程,IO模型
- JDK1.8的新特性详解
- idea gui插件_「Java」 - 自写IntelliJ IDEA插件
- 在 Chrome 调试 Javascript
- Crackme014
- OLAP引擎——Kylin介绍
- 制作一个简单HTML个人网页网页(HTML+CSS)web前端大作业
- 让Google earth叠加中文地图
- P问题、NP问题、NPC问题、NPC-hard问题
- 【JoJo的摄影笔记】黎明女神的呼唤—— 佳能王朝霸业崛起
- CAN总线与CANOPEN协议入门
- linux:使用yum安装_首次使用Linux:30个安装案例
- 计算机网络实验-企业级网络构建与配置实现
- 关于builder模式的使用
热门文章
- 内存管理-基础知识框架和关键结构体(一)
- Linux设备驱动模型-Uevent
- 转:C语言中如何将二维数组作为函数的参数传递
- Android.mk 语法详解
- more than one device and emulator
- 2016年第七届(C/C++)B组蓝桥国赛题
- mysql强制安装参数_Mysql编译安装参数优化
- tensorflow Dataset操作
- Scala 用Option[T] 避免NullPointerException
- 春季高考计算机专业专业分值,春季高考总分多少 分值分布情况如何