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

嵌套类方法引用外部局部变量,必需将声明为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. 一、Linux知识体系结构图

    参考: https://blog.csdn.net/Swing_Liu/article/details/79202479

  2. [POJ 1004] Financial Management C++解题

    参考:https://www.cnblogs.com/BTMaster/p/3525008.html #include <iostream> #include <cstdio> ...

  3. SIMD数据并行(二)——多媒体SIMD扩展指令集

    在计算机体系中,数据并行有两种实现路径:MIMD(Multiple Instruction Multiple Data,多指令流多数据流)和SIMD(Single Instruction Multip ...

  4. POJ 1568 Find the Winning Move

    Find the Winning Move 链接 题意: 4*4的棋盘,给出一个初始局面,问先手有没有必胜策略? 有的话输出第一步下在哪里,如果有多个,按(0, 0), (0, 1), (0, 2), ...

  5. python内置模块[sys,os,os.path,stat]

    python内置模块[sys,os,os.path,stat] 内置模块是python自带功能,在使用内置模块时,需要遵循 先导入在 使用 一.sys 对象 描述 sys.argv 命令行参数获取,返 ...

  6. 【紫书】(UVa1347)Tour

    继续考虑dp题目. 题意分析 其实这里只是更加仔细的做一个lrj的复读机(Orz 他分析了一个很重要的结果:如果是一个人从左到右再回来,并且每个点恰经过一次,那么等价于两个人从左到右每个点经过一次地遍 ...

  7. Module安装

    利用pip3 install numpy,代表安装了数学模块 pip3 install -U numpy,代表升级数学模块 有的时候需要升级pip本身,如上所示,直接执行pip3 install -U ...

  8. Android 之Buletooth

    一:概要: Android提供了Buletooth的API ,通过API 我们可以进行如下的一些操作: 1.扫描其他的蓝牙设备 2.查询能配对的蓝牙设备 3.建立RFCOMM 通道 4.连接其他的蓝牙 ...

  9. nohup追加日志

    背景:用脚本部署Spring Boot应用,用nohup命令进行后台运行 之前的日志处理: nohup command > myout.file 2>&1 & 导致每次用s ...

  10. Python杂篇

    一:文件保存 def save_to_file(file_name, contents): fh = open(file_name, 'w') fh.write(contents) fh.close( ...