咱们一般讲到闭包,通常都是指在javascript的环境中。闭包是JS中一个很是重要的也很是经常使用的概念。闭包产生的缘由就是变量的做用域范围不一样。通常来讲函数内部的定义的变量只有函数内部可见。若是咱们想要在函数外部操做这个变量就须要用到闭包了。javascript
更多精彩内容且看:java
更多内容请访问 www.flydean.com
在JS中,变量能够分为两种全局做用域和局部做用域。在函数外部没法读取函数内部定义的局部变量。git
function f1(){ var n=10; } alert(n); // error
上面的例子中,咱们在函数f1中定义了一个局部变量n,而后尝试从函数外部访问它。结果出错。程序员
虽然函数中定义的变量在函数外部没法被访问。可是在函数中定义的函数中能够访问呀。github
function f1(){ var n=10; function f2(){ alert(n); } return f2; } var result=f1(); result(); // 10
上面的例子中,咱们在f1中定义了f2,在f2中访问了局部变量n。最后将f2返回。接着咱们能够操做返回的函数f2来对函数中定义的局部变量n进行操做。闭包
因此咱们得出了闭包的定义:闭包就是定义在函数内部的函数,或者闭包是可以访问函数局部变量的函数。ide
在lambda表达式出现以前,java中是没有函数的概念的。和函数差很少至关的就是方法了。函数
在方法内部能够定义方法的局部变量。咱们没法在方法内部定义方法,可是咱们能够在方法内部定义匿名类。那么这个匿名类是能够访问方法中定义的局部变量的。以下例所示:区块链
public Runnable createClosureUsingClass(){ int count=10; Runnable runnable= new Runnable() { @Override public void run() { System.out.println(count); } }; return runnable; }
在上面的方法中,咱们定义了一个局部变量count。而后建立了一个匿名类runnable。在runnable中,咱们访问了局部变量count。this
最后将这个建立的匿名类返回。这样返回的匿名类就包含了对方法局部变量的操做,这样就叫作闭包。
在Lambda表达式最佳实践中,咱们介绍了lambda表达式和匿名类的不一样之处在于:
在内部类中,会建立一个新的做用域范围,在这个做用域范围以内,你能够定义新的变量,而且能够用this引用它。
可是在Lambda表达式中,并无定义新的做用域范围,若是在Lambda表达式中使用this,则指向的是外部类。
虽然this的指向是不一样的,可是在lambda表达式中也是能够访问方法的局部变量:
public Runnable createClosureUsingLambda(){ int count=10; Runnable runnable=()-> System.out.println(count); return runnable; }
上面的例子中,咱们在lambda表达式中访问了定义的count变量。
首先lambda表达式是无状态的,由于lambda表达式的本质是函数,它的做用就是在给定输入参数的状况下,输出固定的结果。
若是lambda表达式中引用的方法中的局部变量,则lambda表达式就变成了闭包,由于这个时候lambda表达式是有状态的。咱们接下来用个例子来具体说明。
上面的lambda表达式建立的Runnable,咱们能够这样使用:
public void testClosureLambda(){ Runnable runnable=createClosureUsingLambda(); runnable.run(); }
为了深刻理解lambda表达式和局部变量传值的关系,咱们将编译好的class文件进行反编译。
javap -c -p ClosureUsage
将部分输出结果列出以下:
public java.lang.Runnable createClosureUsingLambda(); Code: 0: bipush 10 2: istore_1 3: iload_1 4: invokedynamic #12, 0 // InvokeDynamic #0:run:(I)Ljava/lang/invokedynamicinvokedynamic; 9: astore_2 10: aload_2 11: areturn private static void lambda$createClosureUsingLambda$0(int); Code: 0: getstatic #29 // Field java/lang/System.out:Ljava/io/PrintStream; 3: iload_0 4: invokevirtual #35 // Method java/io/PrintStream.println:(I)V 7: return
上面咱们列出了createClosureUsingLambda和它内部的lambda表达式的反编译结果。
能够看到在createClosureUsingLambda方法中,咱们首先定义了一个值为10的int,并将其入栈。
再看lambda表达式生成的方法,咱们能够看到这个方法多出了一个int参数,而且经过getstatic命令将参数传递进来。
这就是lambda表达式传递状态的原理。
本文介绍了闭包和lambda表达式之间的关系,并从字节码的角度进一步说明了局部变量是怎么传递给函数内部的lambda表达式的。
本文的例子[https://github.com/ddean2009/
learn-java-base-9-to-20](https://github.com/ddean2009/...
本文做者:flydean程序那些事本文连接:http://www.flydean.com/java-lambda-closure/
本文来源:flydean的博客
欢迎关注个人公众号:程序那些事,更多精彩等着您!