1 定义
闭包又称词法闭包
闭包最早定义为一种包含和的实体.
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。
解释一:闭包是引用了自由变量的函数,这个被引用的变量将和这个函数一同存在。
解释二:闭包是函数和相关引用环境组成的实体。
注::除了局部变量的其他变量
《Python 核心编程》 对 闭包 的解释:
如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被定义为闭包 。定义在外部函数内的但由内部函数引用或者使用的变量被称为自由变量 。
Ruby之父松本行弘在《代码的未来》一书中解释:
闭包就是把函数以及变量包起来,使得变量的生存周期延长。闭包跟面向对象是一棵树上的两条枝,实现的功能是等价的。
简单理解:闭包能够将一个方法作为 一个变量去存储,这个方法有能力去访问所在类的自由变量。
2 闭包的作用
- 让外部访问函数内部变量成为可能;
- 局部变量会常驻在内存中;
- 可以避免使用全局变量,防止全局变量污染;
- 会造成内存泄漏(有一块内存空间被长期占用,而不被释放)
3 JS示例:
function makeFunc() {
var name = 'Mozilla';
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
在makeFunc 执行结束后,我们仍然可以访问name变量,这就是闭包的作用
4 Java中闭包实现
可用内部类、匿名内部类和lamdba表达式来实现闭包。
关键点:
如何用变量去存储方法?
java中能够保存方法的变量指的就是普通的对象
如何让这个普通对象能够访问所在类的自由变量?
纯天然的解决办法是:内部类。内部类能够访问外部类的所有属性及方法。
隐藏具体实现是内部类的作用之一,如何保证隐藏具体实现的同时还能将闭包传递到外部使用?
让内部类实现通用接口,然后将内部类对象向上转型为接口类型。
上述解决办法就是Java最常用的闭包实现办法(内部类+接口)
4.1 内部类+接口实现闭包示例
public class InnerDemo01 {class Bar{public void show(){//do ..}}public static void main(String[] args) {InnerDemo01 innerDemo01=new InnerDemo01();Bar bar=innerDemo01.method();bar.show();//你觉得num会输出吗????}public Bar method(){//String str="wuranghao";int num=30;//局部内部类将输出这个局部变量class innerClass extends Bar{public void show(){System.out.println(num);}}return new innerClass();}}
4.2 lamdba表达式实现闭包示例
public static Supplier<Integer> testClosure() {int i = 1;return () -> {return i;};
}
Java8这里加了一个语法糖:在lambda表达式以及匿名类内部,如果引用某局部变量,则直接将其视为final。
Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。
JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。
5 特性
①函数嵌套函数
②函数内部可以引用函数外部的参数和变量
③参数和变量不会被垃圾回收机制回收
6 优点
①保护函数内的变量安全 ,实现封装,防止变量流入其 他环境发生命名冲突
②在内存中维持一个变量,可以做缓存(但使用多了同时也是一 项缺点,消耗内存)
③匿名执行函数可以减少内存消耗
7 缺点
- 其中一点上面已经有体现了,就是被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏, 解决方法是可以在使用完变量后手动为它赋值为null;
- 其次由于闭包涉及跨域访问,所以会导致性能损失,我们可以 通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响