咱們一般講到閉包,通常都是指在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的博客
歡迎關注個人公衆號:程序那些事,更多精彩等着您!