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

嵌套类方法引用外部局部变量,必需将声明为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. Hive初识(一)

    LOAD DATA语句 一般来说,在SQL创建表后,我们就可以使用INSERT语句插入数据.但在Hive中,可以使用LOAD DATA语句来插入数据. LOAD DATA [LOCAL] INPATH ...

  2. 文件夹选项-安装功能-window服务

    我们初次使用windows10在显示一个文件的时候,可能不会将文件的扩展名显示出来,但是我们很多地方又需要更改文件的扩展名,打开文件的扩展名有两种方式 打开此电脑 ->>>点击右上方 ...

  3. REPLACE(替换字段内容)

    语法: REPLACE <str1> WITH <str2> INTO <c> [LENGTH <l> ]. ABAP/4 搜索字段 <c> ...

  4. Oracle 学习笔记(四)

    ​oracle表查询 使用逻辑操作符号  查询工资高于 500 或者是岗位为 MANAGER 的雇员,同时还要满足他们的姓名首字母为大写 J SELECT * FROM emp WHERE (sal ...

  5. springmvc基础篇—掌握三种控制器

    上一篇文章中我们讲过了处理器的映射,接下来我们来一起学习下springmvc的控制器吧. 首先咱们先创建一个咱们用来测试的实体(model)类: package cn.cfs.springmvc.do ...

  6. Qt Qwdget 汽车仪表知识点拆解8 淡入效果

    先贴上效果图,注意,没有写逻辑,都是乱动的 看下面的开始,开始的时候有一个带入的效果,这里有一个坑, 网上大部分都是调用下面这个函数 setWindowOpacity(); 但是,你会发现,在你的子窗 ...

  7. python+UIAutomation+libary

    #! /usr/bin/env python#Author: XIE TIAN# -*- coding:utf8 -*-from __future__ import unicode_literalsi ...

  8. 2016弱校联盟十一专场10.3 We don't wanna work!

    能把 not working now 写成 not working hard now 还查一晚上也是没谁了 我的做法是维护两个set 分别是前20% 和后80% #include<iostrea ...

  9. python学习总结----内置函数及数据持久化

    抽象基类(了解) - 说明: - 抽象基类就是为了统一接口而存在的 - 它不能进行实例化 - 继承自抽象类的子类必须实现抽象基类的抽象方法 - 示例: from abc import ABC, abs ...

  10. react实现页面切换动画效果

    一.前情概要 注:(我使用的路由是react-router4)     如下图所示,我们需要在页面切换时有一个过渡效果,这样就不会使页面切换显得生硬,用户体验大大提升:     but the 问题是 ...