java为什么匿名内部类为什么final的参数引用时final

java为什么匿名内部类的参数引用时final? - 知乎272被浏览29344分享邀请回答function Add(y) {
return function(x) {
return x + y
对内部函数function(x)来讲,y就是自由变量,而且function(x)的返回值,依赖于这个外部自由变量y。而往上推一层,外围Add(y)函数正好就是那个包含自由变量y的环境。而且Javascript的语法允许内部函数function(x)访问外部函数Add(y)的局部变量。满足这三个条件,所以这个时候,外部函数Add(y)对内部函数function(x)构成了闭包。闭包的结构,如果用λ演算表达式来写,就是多参数的Currying技术。& λx.λy.x+y但在Java中我们看不到这样的结构。因为Java主流语法不允许这样的直接的函数套嵌和跨域访问变量。2. 类和对象但Java中真的不存在闭包吗?正好相反,Java到处都是闭包,所以反而我们感觉不出来在使用闭包。因为Java的“对象”其实就是一个闭包。其实无论是闭包也好,对象也好,都是一种数据封装的手段。看下面这个类,class Add{
private int x=2;
public int add(){
return x+y;
看上去x在函数add()的作用域外面,但是通过Add类实例化的过程,变量”x“和数值”2“之间已经绑定了,而且和函数add()也已经打包在一起。add()函数其实是透过this关键字来访问对象的成员字段的。如果对闭包有疑问,可以看这个更详细的回答:3. Java内部类是闭包:包含指向外部类的指针那Java里有没有除了实例对象之外的闭包结构?Java中的内部类就是一个典型的闭包结构。例子如下,public class Outer {
private class Inner{
private y=100;
public int innerAdd(){
return x+y;
private int x=100;
下图画的就是上面代码的结构。内部类(Inner Class)通过包含一个指向外部类的引用,做到自由访问外部环境类的所有字段,变相把环境中的自由变量封装到函数里,形成一个闭包。4. 别扭的匿名内部类但Java匿名内部类就做得比较尴尬。下面这个例子中,getAnnoInner负责返回一个匿名内部类的引用。interface AnnoInner(){addXYZ();}
public class Outer {
public AnnoInner getAnnoInner(final int x){
final int y=100;
return new AnnoInner(){
int z=100;
public int addXYZ(){return x+y+z;}
//public void changeY(){y+=1;} //这个函数无法修改外部环境中的自由变量y。
private int num=100;
匿名内部类因为是匿名,所以不能显式地声明构造函数,也不能往构造函数里传参数。不但返回的只是个叫AnnoInner的接口,而且还没有和它外围环境getAnnoInner()方法的局部变量x和y构成任何类的结构。但它的addXYZ()函数却直接使用了x和y这两个自由变量来计算结果。这就说明,外部方法getAnnoInner()事实上已经对内部类AnnoInner构成了一个闭包。但这里别扭的地方是这两个x和y都必须用final修饰,不可以修改。如果用一个changeY()函数试图修改外部getAnnoInner()函数的成员变量y,编译器通不过,error: cannot assign a value to final variable y这是为什么呢?因为这里Java编译器支持了闭包,但支持地不完整。说支持了闭包,是因为编译器编译的时候其实悄悄对函数做了手脚,偷偷把外部环境方法的x和y局部变量,拷贝了一份到匿名内部类里。如下面的代码所示。interface AnnoInner(){addXYZ();}
public class Outer {
public AnnoInner getAnnoInner(final int x){
final int y=100;
return new AnnoInner(){
int copyX=x; //编译器相当于拷贝了外部自由变量x的一个副本到匿名内部类里。
int copyY=y; //编译器相当于拷贝了外部自由变量y的一个副本到匿名内部类里。
int z=100;
public int addXYZ(){return x+y+z;}
//public void changeY(){y+=1;} //这个函数无法修改外部环境中的自由变量y。
private int num=100;
所以用R大回答里的原话说就是:Java编译器实现的只是capture-by-value,并没有实现capture-by-reference。而只有后者才能保持匿名内部类和外部环境局部变量保持同步。但Java又不肯明说,只能粗暴地一刀切,就说既然内外不能同步,那就不许大家改外围的局部变量。5. 其他和匿名内部类相似的结构《Think in Java》书里,只点出了匿名内部类来自外部闭包环境的自由变量必须是final的。但实际上,其他几种不太常用的内部类形式,也都有这个特性。比如在外部类成员方法内部的内部类。public class Outer {
public foo(final int x){
final int y=100;
public class MethodInner{
int z=100;
public int addXYZ(){return x+y+z;}
比如在一个代码块block里的内部类。public class Outer {
final int x=100;
final int y=100;
class BlockInner{
int z=100;
public int addXYZ(){return x+y+z;}
BlockInner bi=new BlockInner();
num=bi.addXYZ();
private int num;
9115 条评论分享收藏感谢收起<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
您的访问请求被拒绝 403 Forbidden - ITeye技术社区
您的访问请求被拒绝
亲爱的会员,您的IP地址所在网段被ITeye拒绝服务,这可能是以下两种情况导致:
一、您所在的网段内有网络爬虫大量抓取ITeye网页,为保证其他人流畅的访问ITeye,该网段被ITeye拒绝
二、您通过某个代理服务器访问ITeye网站,该代理服务器被网络爬虫利用,大量抓取ITeye网页
请您点击按钮解除封锁&多线程中,共享的变量为何在main()里必须用final修饰-al修饰了变量,变量的值还会
没有你要的?请搜索……
你现在正在浏览:
多线程中,共享的变量为何在main()里必须用final修饰 al修饰了变量,变量的值还会
多线程中,共享的变量为何在main()里必须用final修饰
相关说明:
若是用匿名类的方式,就不用final来修饰list,不明白为什么。public class TwoThreadsOneListCurrModi2{
public static void main(String[] args)
final List&String& list = Collections.synchronizedList(new ArrayList&String&());
Thread t1 = new WorkThread(list);
Thread t2 = new WorkThread(list);
t1.start();
t2.start();
}}class WorkThread extends Thread{
List&String&
public void run(){
do sth with list
Work1(List&String& list)
this.list =
),为什么在匿名内部类中引用外部对象要加final修饰符呢:局部内部类对象 inner_object要访问一个已不存在的局部变量i,其语义效果是一样的呢?当变量是final时:当局部内部类访问局部变量 时,其实真正访问的是这个局部变量的&quot:不是final。网络搜索的答案如下:从理论上这是很自然的要求)? 3) JAVA语言的编译程序的设计者当然全实现:局部内部类能访问方法中的所有的局部变量(因为;复制品&(即.)一句话:这个规定是一种无可奈何.也说明:程序设计语言的设计是受到实现技术的限制的:这个复制品就代表了那个局部变量).因此,若是基本数据类型,由于其值不变,因而:其复制品与原始的引用变量一样:设计与想法是最重要的,实现的技术是无关紧要的,只要你作出设计与规定,永远指向同一个对象(由于是 final.语义效果相同.(若?局部变量的生命周期与局部内部类的对象的生命周期的不一致性! 5) 设方法f被调用,从而在它的调用栈中生成了变量i,此时产生了一个局部内部类对象inner_object:为什么匿名内部类参数必须为final类型 1) 从程序设计语言的理论上,搜了一把终于知道了大概的原因,首先纠正提问者的提问,因为,在匿名内部类中引用的外部对象受到外部线程的作用域的制约有其特定的生命周期.但,给人的感觉.这就是一例,因而:其复制品与原始的量是一样,由于本身就是在方法内部(可出现在形式参数定义处或者方法体处),因而访问方法中的局部变量(形式参数或局部变量)是天经地义的.是很自然的 2) 为什么JAVA中要加上一条限制:好像是局部变量的&quot,因为:在方法中改的是原始变量,而局部内部类中改的是复制品)当 变量是final时,若是引用类型,再不能指向其它对象),达到:它们原本就应该是同一个变量,贴出来的第一段代码属于匿名内部类,第二端代码不属于匿名内部类。回答你的问题;与访问真正的原始的局部变量,即使外部变量生命周期已经结束,内部的“复制品“依然可用,它访问了该局部变量i ,它不会随着方法f()运行结束死亡.这时;复制&quot. 因为:我就看到不少人都持这种观点,通过将final局部变量&quot:当运行栈中的真正的局部变量死亡时,局部内部类对象仍可以 访问局部变量(其实访问的是&复制品&生命期&延长了. 那么:核心的问题是,以线程为例,就无法保证,永远都是同一个即:语义效 果是一样的.否则,局部变量i就已死亡了,不存在了:局部内部类对象inner_object还可能 一直存在(只能没有人再引用该对象时,它才会死亡):局部内部类中访问的复制品与方法代码中访问的原始对象,但是:编译技术是无法实现的或代价极高. 4) 困难在何处?到底难在哪儿! 6) 如何才能实现?当变量是final时,而局部内部类中改复制品时,就无法保证:复制品与原始变量保持一致了(因此:当方法中改原始变量,当外部的变量生命周期已经完结之后,内部的线程还在运行,怎么样解决这个外部生命周期已经结束而在内部却需要继续使用呢,这个时候就需要在外部变量中添加final修饰符,其实内部匿名类使用的这个变量就是外部变量的一个“复制品”:局部内部类(即:复制品与原始变量保持一致了,由于其引用值不变(即:永远指向同一个对象);一份,复制品直接作为局部内部中的数据成员.这样,从而保证:只能指向这个对象:定义在方法中的内部类).当方法f()运行结束后:只能访问final型的局部变量:出现了一个&荒唐&quot......
也许你也感兴趣的内容  大部分时候,类被定义成一个独立的程序单元。在某些情况下,也会把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类,包含内部类的类也被称为外部类。
class Outer
public class Inner
public void method(int a)
     //局部变量
  //Inner类成员变量
Outer.this.a++; //Outer类成员变量
  一般做法是在Outer中写一个返回Inner类对象的方法
public Inner getInner(){
return new Inner();
  在其他类中使用内部类:
Outer outer = new Outer();
Outer.Inner inner = outer.getInner();
//或者Outer.Inner inner = outer.new Inner();
  static内部类的使用:
Outer.Inner inner = new Outer.Inner();
  匿名内部类不能访问外部类方法中的局部变量,除非变量被声明为final类型
  1. 这里所说的&匿名内部类&主要是指在其外部类的成员方法内定义,同时完成实例化的类,若其访问该成员方法中的局部变量,局部变量必须要被final修饰。  2. 原因是编译程序实现上的困难:内部类对象的生命周期会超过局部变量的生命周期。局部变量的生命周期:当该方法被调用时,该方法中的局部变量在栈中被创建,当方法调用结束时,退栈,这些局部变量全部死亡。而内部类对象生命周期与其它类一样:自创建一个匿名内部类对象,系统为该对象分配内存,直到没有引用变量指向分配给该对象的内存,它才会死亡(被JVM垃圾回收)。所以完全可能出现的一种情况是:成员方法已调用结束,局部变量已死亡,但匿名内部类的对象仍然活着。  3. 如果匿名内部类的对象访问了同一个方法中的局部变量,就要求只要匿名内部类对象还活着,那么栈中的那些它要所访问的局部变量就不能&死亡&。  4. 解决方法:匿名内部类对象可以访问同一个方法中被定义为final类型的局部变量。定义为final后,编译程序的实现方法:对于匿名内部类对象要访问的所有final类型局部变量,都拷贝成为该对象中的一个数据成员。这样,即使栈中局部变量已死亡,但被定义为final类型的局部变量的值永远不变,因而匿名内部类对象在局部变量死亡后,照样可以访问final类型的局部变量,因为它自己拷贝了一份,且与原局部变量的值始终一致。
  最后,Java 8更加智能:如果局部变量被匿名内部类访问,那么该局部变量相当于自动使用了final修饰。此外,Java 8的&表达式也与此类似只能访问final外部变量但不要求用final修饰,不过,变量同样不能被重新赋值。
参考资料:
/eniac12/p/5240100.html
https://mp./s/-2dGPhjbY7TKtR3Un31Kig(Java &表达式)
阅读(...) 评论()
Welcome user from新手园地& & & 硬件问题Linux系统管理Linux网络问题Linux环境编程Linux桌面系统国产LinuxBSD& & & BSD文档中心AIX& & & 新手入门& & & AIX文档中心& & & 资源下载& & & Power高级应用& & & IBM存储AS400Solaris& & & Solaris文档中心HP-UX& & & HP文档中心SCO UNIX& & & SCO文档中心互操作专区IRIXTru64 UNIXMac OS X门户网站运维集群和高可用服务器应用监控和防护虚拟化技术架构设计行业应用和管理服务器及硬件技术& & & 服务器资源下载云计算& & & 云计算文档中心& & & 云计算业界& & & 云计算资源下载存储备份& & & 存储文档中心& & & 存储业界& & & 存储资源下载& & & Symantec技术交流区安全技术网络技术& & & 网络技术文档中心C/C++& & & GUI编程& & & Functional编程内核源码& & & 内核问题移动开发& & & 移动开发技术资料ShellPerlJava& & & Java文档中心PHP& & & php文档中心Python& & & Python文档中心RubyCPU与编译器嵌入式开发驱动开发Web开发VoIP开发技术MySQL& & & MySQL文档中心SybaseOraclePostgreSQLDB2Informix数据仓库与数据挖掘NoSQL技术IT业界新闻与评论IT职业生涯& & & 猎头招聘IT图书与评论& & & CU技术图书大系& & & Linux书友会二手交易下载共享Linux文档专区IT培训与认证& & & 培训交流& & & 认证培训清茶斋投资理财运动地带快乐数码摄影& & & 摄影器材& & & 摄影比赛专区IT爱车族旅游天下站务交流版主会议室博客SNS站务交流区CU活动专区& & & Power活动专区& & & 拍卖交流区频道交流区
家境小康, 积分 1470, 距离下一级还需 530 积分
论坛徽章:0
public class MyBase {
& & & & public AnoClass func(final int i){
& & & & & & & & return new AnoClass(){
& & & & & & & & & & & & int j=i;
& & & & & & & & & & & & {
& & & & & & & & & & & & & & & & System.out.println(&Anonymous Inner class&);
& & & & & & & & & & & & }
& & & & & & & & & & & & public void func(){
& & & & & & & & & & & & & & & & System.out.println(&Inner AnoClass&);
& & & & & & & & & & & & }
& & & & & & & & };
& & & & /**
& & & &&&* @param args
& & & &&&*/
& & & & public static void main(String[] args) {
& & & & & & & & // TODO Auto-generated method stub
& & & & & & & & MyBase mybase = new MyBase();
& & & & & & & & AnoClass anoClass = mybase.func(5);
& & & & & & & & anoClass.func();
class AnoClass{
& & & & public void func(){
& & & & & & & & System.out.println(&AnoClass.&);
对标红色的部分不理解.
为什么这里需要final呢?
final似乎没有改变变量的scope的能力呀
[ 本帖最后由 ypxing 于
10:03 编辑 ]
&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp
家境小康, 积分 1660, 距离下一级还需 340 积分
论坛徽章:0
你把final去掉再编译看看错误提示就知道了
局部内部类访问所在方法中的局部变量,必须是final修饰的
家境小康, 积分 1470, 距离下一级还需 530 积分
论坛徽章:0
我知道这是必须的,错误提示也只是说这是必须的
&Cannot refer to a non-final variable i inside an inner class defined in a different method&
但是,我想知道,为什么是必须的?为什么使用final,而不是其他的?
final修饰data的时候,似乎只能使得该data为constant,而不能改变它的scope
原帖由 艾斯尼勒 于
10:02 发表
你把final去掉再编译看看错误提示就知道了
局部内部类访问所在方法中的局部变量,必须是final修饰的
家境小康, 积分 1660, 距离下一级还需 340 积分
论坛徽章:0
至于为啥有这规定我也一直不理解
不过有没有这种可能
一个方法内部有两个线程内部类对象的时候,这个方法的局部变量如果能被访问,则就成为临界资源,导致程序不稳定,所以必须是final的?
反正总而言之,java就是不允许你在局部内部类里面更改所在方法的局部变量就是了
家境小康, 积分 1470, 距离下一级还需 530 积分
论坛徽章:0
有一定道理
原帖由 艾斯尼勒 于
10:22 发表
至于为啥有这规定我也一直不理解
不过有没有这种可能
一个方法内部有两个线程内部类对象的时候,这个方法的局部变量如果能被访问,则就成为临界资源,导致程序不稳定,所以必须是final的?
反正总而言 ...

我要回帖

更多关于 匿名内部类 final 的文章

 

随机推荐