为什么需要闭包
https://blog.csdn.net/mojir/article/details/103880381
首先来看一个例子,我们来实现一个计数器
var counter = 0;
function add() {
return counter += 1;
}
add();
add();
add();// 计数器现在为 3
现在我们已经达到了目的,可是问题来了,代码中的任何一个函数都可以随意改变全局变量counter的值,所以这个计数器并不完美。那我们把counter放在add函数里面不就好了么?
function add() {
var counter = 0;
return counter += 1;
}
add();
add();
add();// 本意是想输出 3, 但输出的都是 1
所以这样做的话,每次调用add函数,counter的值都要被初始化为0,还是达不到我们的目的
所以这时候我们就要用闭包去解决这个问题
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add();
add();
add();// 计数器为 3
这时候我们完美实现了计数器。这段非常精简,可以拆分成如下等价代码
function outerFunction () {
var counter = 0;
function innerFunction (){
return counter += 1;
}
return innerFunction;
}
var add = outerFunction();
add();
add();
add();// 计数器为 3
这时候的add就形成了一个闭包。一个闭包由两部分组成,函数和创建该函数的环境。环境是由环境中的局部变量组成的。对于闭包add来说,它由函数innerFunction和变量counter组成,所以这时候add是可以访问变量counter的
什么是闭包?
1.闭包的本质就是代码片断。如果一个变量代表的是一段文字,一个数字。数学、字符串可以用变量来存载。而代码用的则是闭包。所以闭包可以理解成一个代码片断的引用。
2.最早的代码片段出现在c语言里,也就是函数指针,变量指针如char*代表字符串。而函数的指针则是代表的一个函数或者说一段代码。
3.大家都知道函数的参数有int型,char型这些。但如果传的是一个方法为参数。这时这个参数的类型就是函数指针,而传的具体的方法,我们就把它叫做闭包。
4.java在jdk8以前不支持函数指针所以使用内部类加外部接口的反向实现。
5.闭包可以说就是一段代码(有别于字符串反射的代码)。举例:A让B做一件事情,一种不停的去问B做完了没有。另一种就是A写一个接口,让B做完了去调用这个接口,即B做完了后去调用这个接口。然后A只需要实现这个接口,而这个接口的实现代码里就是B做完了的事情,这就是接口反向调用,而这也是闭包的最经典应用。
6.闭包的功能就是使一个函数能访问另一个函数作用域中的变量。形成闭包之后,该变量不会被垃圾回收机制回收。
7.闭包的原理其实还是作用域。
- 使用闭包的优点是可以避免全局变量污染,缺点是容易造成内存泄露(不再用到的内存,没有及时释放,就叫做内存泄漏)
JAVA中的闭包
https://www.cnblogs.com/chenjunbiao/archive/2011/01/26/1944417.html
1、内部类。
顾名思义,内部类就是将一个类定义在另一个类的内部。在JAVA中,内部类可以访问到外围类的变量、方法或者其它内部类等所有成员,即使它被定义成private了,但是外部类不能访问内部类中的变量。这样通过内部类就可以提供一种代码隐藏和代码组织的机制,并且这些被组织的代码段还可以自由的访问到包含该内部类的外围上下文环境。
在JAVA中,闭包是通过“接口+内部类”实现
public class DemoClass1 {
private int length =0; //private|public
private class InnerClass implements ILog {
@Override
public void Write(String message) {
//DemoClass1.this.length = message.length();
length = message.length();
System.out.println("DemoClass1.InnerClass:" + length);
}
}
public ILog logger() {
return new InnerClass();
}
public static void main(String[] args){
DemoClass1 demoClass1 = new DemoClass1();
demoClass1.logger().Write("abc");
//.new
DemoClass1 dc1 = new DemoClass1();
InnerClass ic = dc1.new InnerClass();
ic.Write("abcde");
}
}
该例子的主要功能是实现一个写日志的ILog接口,但是该接口的类被定义在DemoClass1这个外围类中了,而且这个InnerClass内部类还可以访问其外围类中的私有变量length。
1.1、.new
从上面的例子可见,InnerClass是定义在DemoClass1内部的一个内部类,而且InnerClass还可以是Private。
如何创建这个InnerClass的实例? 可以通过外围类的实例进行创建,如:
DemoClass1 dc1 = new DemoClass1();
InnerClass ic = dc1.new InnerClass();
ic.Write("abcde");
1.2、.this
如何通过this显式引用外围类的变量?通过此格式进行引用:{外围类名}.this.{变量名称}。如:
DemoClass1.this.length = message.length();
2、局部内部类。
局部内部类是指在方法的作用域内定义的的内部类。
public class DemoClass2 {
private int length =0;
public ILog logger() {
//在方法体的作用域中定义此局部内部类
class InnerClass implements ILog {
@Override
public void Write(String message) {
length = message.length();
System.out.println("DemoClass2.InnerClass:" + length);
}
}
return new InnerClass();
}
}
因为InnerClass类是定义在logger()方法体之内,所以InnerClass类在方法的外围是不可见的。
3、匿名内部类。
顾名思义,匿名内部类就是匿名、没有名字的内部类,通过匿名内部类可以更加简洁的创建一个内部类。
public class DemoClass3 {
private int length =0;
public ILog logger() {
return new ILog() {
@Override
public void Write(String message) {
length = message.length();
System.out.println("DemoClass3.AnonymousClass:" + length);
}
};
}
}
public class AnonymousInnerClassCalculator {
int x;
int result;
Calculable innerCal = new Calculable(){
int addWith(int y) {
System.out.println("自由变量 x="+x);
return x+y;
}
};
public static void main(String[] args) {
AnonymousInnerClassCalculator cal = new AnonymousInnerClassCalculator();
cal.x = 1;
int result = cal.innerCal.addWith(2);
System.out.println("1 + 2 = " +result);
}
}
abstract class Calculable{
abstract int addWith(int y);
}
4、实例初始化。
匿名类的实例初始化相当于构造器的作用,但不能重载。
public ILog logger(final int level) throws Exception {
return new ILog() {
{
//实例初始化,不能重载
if(level !=1)
throw new Exception("日志等级不正确!");
}
@Override
public void Write(String message) {
length = message.length();
System.out.println("DemoClass5.AnonymousClass:" + length);
}
};
}
匿名内部类的实例初始化工作可以通过符号 {...} 来标记,可以在匿名内部类实例化时进行一些初始化的工作,但是因为匿名内部类没有名称,所以不能进行重载,如果必须进行重载,只能定义成命名的内部类。
为什么需要闭包。
https://blog.csdn.net/mojir/article/details/103880381
闭包的价值在于可以作为函数对象或者匿名函数,持有上下文数据,作为第一级对象进行传递和保存。闭包广泛用于回调函数、函数式编程中。