博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java内部类访问局部变量时的final问题
阅读量:6654 次
发布时间:2019-06-25

本文共 2437 字,大约阅读时间需要 8 分钟。

JAVA用了也快三年了,内部类访问局部变量的情况也没少遇到。也一直知道要给变量加个final修饰符,不然通过不了编译。但一直也没深究过为什么要加。昨天好奇的上网查了下,并翻阅了下相关的书籍(Core Java 8th),终于算是搞明白了,在这里简单说明下。

说先我们来看一段示例代码:

Java语言
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文件。其形式如下(类名为:外部类$内部类):

Java语言
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编译器还偷偷做了一些额外的工作。编译器除了上文提到的会生成一个内部类类,同时还会修改我们的外部类代码。其修改如下:

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时就遇到了,期间也有想过为什么要加,但最终都没有深究,就理所当然的认为是规定了。其实这是一个很不好的习惯。学习东西就要“知其然,知其所以然”,一知半解最是害人。希望大家都可以引以为鉴。

 (http://lidejiasw.wordpress.com/2011/06/08/java%E5%86%85%E9%83%A8%E7%B1%BB%E8%AE%BF%E9%97%AE%E5%B1%80%E9%83%A8%E5%8F%98%E9%87) 

转载于:https://www.cnblogs.com/talk/archive/2012/10/18/2729930.html

你可能感兴趣的文章
多台Mac电脑使用同一个apple开发者账号测试
查看>>
区块链发行币说明参考
查看>>
图解git中的最常用命令
查看>>
Expo大作战(七)--expo如何使用Genymotion模拟器
查看>>
[svc]linux文件权限
查看>>
R绘图 第一篇:ggplot2绘图
查看>>
关于使用maven打包如何聚合资源文件
查看>>
互联网项目从产品设计到上线的过程是怎么样的?
查看>>
浮动、定位
查看>>
php解析mpp文件中的前置任务
查看>>
PyTorch为何如此高效好用?
查看>>
[ES6] Proxy & Reflect
查看>>
关于Go,你可能不注意的7件事
查看>>
图片合并成PDF,两个PDF的合并
查看>>
html input type=file 选择图片,图片预览 纯html js实现图片预览
查看>>
相机上的P,S,A,M分别是什么单词的缩写?
查看>>
杭州电子科技大学2018年自命题科目考试大纲(数据结构与组成原理)
查看>>
django 的缩略图sorl-thumbnail的使用连接地址
查看>>
WPF:如何为程序添加splashScreen?
查看>>
用js生成PDF的方案
查看>>