1 public class ClosureBeforeJava8 { 2 int y = 1; 3 4 public static void main(String[] args) { 5 final int x = 0; 6 ClosureBeforeJava8 closureBeforeJava8 = new ClosureBeforeJava8(); 7 Runnable run = closureBeforeJava8.getRunnable(); 8 new Thread(run).start(); 9 } 10 11 public Runnable getRunnable() { 12 final int x = 0; 13 Runnable run = new Runnable() { 14 @Override 15 public void run() { 16
17 System.out.println("local varable x is:" + x); 18 //System.out.println("member varable y is:" + this.y); //error 19 } 20 }; 21 return run; 22 } 23 }
上段代碼的輸出:local varable x is:0java
在代碼的第13行到第20行,經過匿名類的方式實現了Runnable接口的run()方法,實現了一部分操做的集合(run方法),並將這些操做映射爲java的對象,在java中就能夠實現將函數以變量的方式進行傳遞了,若是僅僅是傳遞函數指針,那還不能算是閉包,咱們再注意第17行代碼,在這段被封裝能夠在不一樣的java對象間傳遞的代碼,引用了上層方法的局部變量,這個就有些閉包的意思在裏面了。可是第18行被註釋掉的代碼在匿名類的狀況下卻沒法編譯經過,也就是封裝的函數裏面,沒法引用上層方法所在對象的成員變量。總結一下,java8以前的閉包特色以下:程序員
1.能夠實現封裝的函數在jvm裏進行傳遞,能夠在不一樣的對象裏進行調用;golang
2.被封裝的函數,能夠調用上層的方法裏的局部變量,可是此局部變量必須爲final,也就是不能夠更改的(基礎類型不能夠更改,引用類型不能夠變動地址);多線程
java8裏對於閉包的支持,其實也就是lamda表達式,咱們再來看一下上段代碼在lamda表達式方式下的寫法:閉包
1 public class ClosureInJava8 { 2 int y = 1; 3 4 public static void main(String[] args) throws Exception{ 5 final int x = 0; 6 ClosureInJava8 closureInJava8 = new ClosureInJava8(); 7 Runnable run = closureInJava8.getRunnable(); 8 Thread thread1 = new Thread(run); 9 thread1.start(); 10 thread1.join(); 11 new Thread(run).start(); 12 } 13 14 public Runnable getRunnable() { 15 final int x = 0; 16 Runnable run = () -> { 17
18 System.out.println("local varable x is:" + x); 19 System.out.println("member varable y is:" + this.y++); 20 }; 21 return run; 22 } 23 }
上面對代碼輸出:併發
local varable x is:0
member varable y is:1
local varable x is:0
member varable y is:2jvm
在代碼的第16行到第20行,經過lamda表達式的方式實現了函數的封裝(關於lamda表達式的用法,你們能夠自行google)。經過代碼的輸出,你們能夠發現,在lamda表達式的書寫方式下,封裝函數不但能夠引用上層方法的effectively final類型(java8的特性之一,其實也是final類型)的局部變量,還能夠引用上層方法所在對象的成員變量,並能夠在其它線程和方法中對此成員變量進行修改。總結一下:java8對於閉包支持的特色以下:ide
1.經過lamda表達式的方式能夠實現函數的封裝,並能夠在jvm裏進行傳遞;函數
2.lamda表達式,能夠調用上層的方法裏的局部變量,可是此局部變量必須爲final或者是effectively final,也就是不能夠更改的(基礎類型不能夠更改,引用類型不能夠變動地址);學習
golang裏對於閉包的支持,理解起來就很是容易了,就是函數能夠做爲變量來傳遞使用,代碼以下:
1 package main 2 3 import "fmt" 4 5 func main() { 6 ch := make(chan int ,1) 7 ch2 := make(chan int ,1) 8 fn := closureGet() 9 go func() { 10 fn() 11 ch <-1 12 }() 13 go func() { 14 fn() 15 ch2 <-1 16 }() 17 <-ch 18 <-ch2 19 } 20 21 func closureGet() func(){ 22 x := 1 23 y := 2 24 fn := func(){ 25 x = x +y 26 fmt.Printf("local varable x is:%d y is:%d \n", x, y) 27 } 28 return fn 29 }
代碼輸出以下:
local varable x is:3 y is:2
local varable x is:5 y is:2
代碼的第24行到27行,定義了一個方法fn,此方法可使用上層方法的局部變量,總結一下:
1.golang的閉包在表達形式上,理解起來很是容易,就是函數能夠做爲變量,來直接傳遞;
2.golang的封裝函數能夠沒有限制的使用上層函數裏的局部變量,而且在不一樣的goroutine裏修改的值,都會有所體現。
關於第2點,你們能夠參考文章:https://studygolang.com/articles/11627 中關於golang閉包的講解部分。
golang的閉包從語言的簡潔性、理解的難易程度、支持的力度上來講,確實仍是優於java的。本文做爲java和golang對比分析的第一篇文章,因爲調研分析的時間有限,不免有疏忽之處,歡迎各位指正。