一、前言
我们知道JAVA参数是值传递,但是偶然发现java中参数还能加final
修饰符,那么这个final
修饰符有什么作用呢?
- 既然java是值传递,那么将参数限定为
final
与否根本没有任何意义——就算不限定也不会改变实参的指向。 - 但是存在则必有其合理性,这个合理性还要从匿名内部类说起。
二、匿名内部类
什么是匿名内部类
-
没有名字
-
必须继承一个类或实现一个接口
匿名内部类是语法糖
我们知道java的Lambda表达式是匿名内部类的语法糖,只不过不再绑定this
(this
指向调用它的外部类对象)。但实际上,匿名内部类本身也是一个语法糖。匿名内部类会被编译为继承目标类或实现目标接口的内部类。
生命周期
因为对象的生命周期是大于方法的生命周期的。所以内部类的对象在方法已经结束以后,可能还会访问方法,那么就会出现问题。所以实现的思路就是将匿名内部类使用到的局部变量以构造方法的形式拷贝到内部类中。表现的效果就是——提高了局部变量的生命周期
问题
语法糖带来便捷性的同时,也容易带来误解。比如说很可能写着写着就想当然的以为内部类对象的方法运行的时候外部方法还没有退出。实际上极大可能性的这个方法已经出栈并且释放了所有局部变量的内存。所以强制使用final
限定这些拷贝到内部类中的局部变量。约定这些变量只读不写
JDK8
JAVA8以后已经默认在匿名内部类访问的局部变量上final
限定。但是虽然我们不用再显式用final
修饰了,我们仍然不能去改变这些隐式常量
三、闭包
什么是闭包
1、百度百科
闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
2、简而言之
闭包就是能够读取局部变量的函数
JAVA的闭包
JAVA的匿名内部类也可以称之为不完整的闭包。JAVA的局部变量存放在栈中,为了实现闭包,必须将栈中的局部变量拷贝到匿名内部类对象中(堆中)。因此无论任何技术都无法使得它们双向绑定,所以必须final
限定,使其永远不可能触发不同步的问题
JS的闭包
同样,JS中函数的局部变量也是存放在栈中。但不同的是,JS中的局部变量在栈退出时会将可能被引用的内存和可能被访问的内存保存下来。这为闭包与局部变量双向绑定的实现提供了可能
只要存在调用内部函数的可能,JavaScript就需要保留被引用的函数。
而且JavaScript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量废弃,JavaScript的垃圾收集器才能释放相应的内存空间
为什么JAVA不这样实现?
- 闭包有内存泄漏的风险
- 可能有悖于设计理念吧
- 咱也不知道咱也不敢问啊