嵌套类(内部类)方法安全引用外部方法局部变量的原理

嵌套类方法引用外部局部变量,必需将声明为final,否则将出现 Cannot refer to a non-final variable * inside an inner class defined in a different method 编译错误,错误的直接原因是嵌套类对象生命周期与外部方法局部变量生命周期不一致,当外部方法执行完毕,局部变量自动回收,而方法执行产生的新对象不一定会被GC回收(当该对象已被外部对象变量引用时),该对象存续期间,因调用自身方法而引用到已被回收的局部变量,会导致空指针BUG。

给个简单示例:

     public static void main(String[] args) {
ArrayList<Father> list = new ArrayList<>();
for(int i = 0;i<10;i++){
list.add(showA(i));
}
for(int i = 0;i<10;i++){
list.get(i).show();
}
} static abstract class Father{
abstract void show();
} static Father showA(final int num){
class A extends Father{
void show(){
System.out.println(num);
}
}
A a = new A();
a.show();
return a;
}

代码执行两个循环,第一个循环使用方法showA创建对象,打印传入变量final int num,并将对象保存在外部列表list中,第二个循环遍历list,调取对象并执行对象内打印方法再次打印之前的变量值。

当进行第二个for循环时,不像在第一个for循环中在showA方法里执行,此时局部变量num自然不存在(在第一次循环结束后,showA方法执行完毕自动回收了,局部变量num与对象a生命周期不一致!),假设编译器允许showA直接传入int num不声明为final,则第二个for循环中调用a.show()必然触发Null Pointer BUG,但声明为final后,仍然能够打印之前被传入的final int num变量值。

通过设置断点调试可以发现,被声明为final的局部变量在被内部类方法引用时,内部类对象会自动在自身内部设入private final字段保存该变量值(如下图示,事实上是在内部类实例化时通过内部默认构造器完成),以延续该变量值的生命周期,以后对象使用该变量不再受外部局部变量生命周期干扰。

关于声明为 final 类型的必要性

至此,内部类对外部方法局部变量引用原理已经阐明,但仍未解释说明为何必需要将此局部变量声明为final类型。刚刚关注到网上的博文,恍然大悟了一下,借用其思想转述一下:

因为上文提及到,内部类对外部方法局部变量引用进行自动转存的“编译器设计问题”,并没有显式告诉程序员:外部方法中的局部变量与内部类方法引用的变量存在事实上的不同(这句话很拗口..)。如果在一方改变该变量值,将没有任何额外代码保证两边变量的同步性,表现出来的现象就是,内部类初始化后,再次修改局部变量,内部类方法打印出的变量值出现与实际外部方法局部变量值不一致的莫名其妙的错误。因此,java在编译前就直接强制要求,引用的局部变量必需声明为final类型,不允许进行二次修改。

关于final局部变量引用的研究的更多相关文章

  1. 局部内部类为什么只能访问final局部变量,对于成员变量却可以随便访问?

    局部内部类为什么只能访问final局部变量,对于成员变量却可以随便访问? public class OuterClass { private int memberField = 10; public ...

  2. python之局部变量引用赋值前的结果

    通过正则表达式,实现加减 昨晚在做计算器的时候,被一个BUG搞懵比了.现在再看看,发现我好小白啊~~ #++- num = input("please input:") sa = ...

  3. javascript中值传递与值引用的研究

    今天重新看了一下<javascript高级程序设计>,其中讲到了javascript中的值传递和值引用,所以就自己研读了一下,但是刚开始没有明白函数中的参数只有值传递,有的场景好像参数是以 ...

  4. Java中final修饰符深入研究

    一.开篇 本博客来自:http://www.cnblogs.com/yuananyun/ final修饰符是Java中比较简单常用的修饰符,同时也是一个被"误解"较多的修饰符.对很 ...

  5. python局部变量引用问题

    a = [1, 2] b = 'Immutable' def test(): # global b print(a) a.append('asd') b = b + 'asd' # 当只是引用变量b的 ...

  6. Block循环引用问题研究

    自从苹果在objc中添加Block功能支持以后已经过了很久.目前网上对于Block的使用有很多介绍.不过对于Block的内存管理问题,则是众说纷纭.再加上objc开始使用ARC以后,对于Block的内 ...

  7. 局部内部类访问方法中的局部变量为什么加final

    转载:http://www.cnblogs.com/mjblogs/p/4971630.html 1)从程序设计语言的理论上:局部内部类(即:定义在方法中的内部类),由于本身就是在方法内部(可出现在形 ...

  8. final修饰符(3)-基本类型变量和引用类型变量的区别

    final修饰基本类型变量 当使用final修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变 final修饰引用类型变量 当使用final修饰引用类型变量时,它保存的仅仅是一 ...

  9. 深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)

    作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout-language- ...

随机推荐

  1. python网络编程,通过服务名称和会话类型(tcp,udp)获取端口号,简单的异常处理

    作为一个php程序员,同时有对网络方面感兴趣,php就比较蛋疼了,所以就抽了些时间看python 之前学python基础因为工作原因,断断续续的看了个基础,差不多是可以写代码了 最近在看<pyt ...

  2. HyperLedger Fabric 1.4 区块链应用场景(3.1)

    比特币是区块链应用最早的场景,随着比特币安全稳定运行多年以后,数字货币的场景应用遍地开花,各种山寨币泛滥,通过ICO(Initial Coin Offering 首次币发行)就能融到大量资金,上市后的 ...

  3. codeforces 845A Chess Tourney

    参考:https://blog.csdn.net/zhongyuchen/article/details/77478039 #include <iostream> #include < ...

  4. SGU 495

    #include<bits/stdc++.h> using namespace std; #define ll long long ; ; int n,m; double dp[N]; / ...

  5. urllib,url中链接包含汉字怎么用百分号(%)加密处理

    使用urllib中的quote,和unquote方法将汉字编码成gbk(2个百分号对应一个汉字)或者utf8(3个百分号对应一个汉字) 注意用%加密汉字时,汉字不能是Unicode编码格式,否则会报错 ...

  6. Python:正则表达式—— re 模块

    一.什么是正则表达式(Regular Expression) 正则表达式本身是一种小型的.高度专业化的编程语言,它内嵌在Python中,并通过 re(regular expression)模块实现.使 ...

  7. 从C到C++ (2)

    从C到C++ (2) 一.    C++中增加了作用域标示符 :: 1.     用于对局部变量同名的全局变量进行访问. 2.     用于表示类成员. 二.    new.delete运算符 1.  ...

  8. ExtJs工具篇(2)——Aptana Studio 3 汉化

    本身用的是中文版本的,但是输入一些中文后,竟然有乱码,所以就想把它汉化.在网上搜索了一下,把步骤记录如下: 首先到这个网站去 http://aptana.com/support 选择View Docu ...

  9. 玩转Vim-札记(二)

    玩转Vim-札记(二) 距上篇博文已有一周有余,上次主要介绍了编辑器之神Vim的起源.安装并介绍了两种模式以及一些简单的操作.本次将继续对Vim的使用进行介绍. 登堂入室 首先接着说移动吧: 0 → ...

  10. Ubuntu 手机 app开发学习0

    # 相关网址 http://developer.ubuntu.com/zh-cn/apps/sdk/ 0. 环境搭建 首选需要一个Ubuntu 14.04操作系统.没啥好讲的,直接安装了一个虚拟机. ...