第一章.java&golang的區別之:閉包

對於golang一直存有覬覦之心,但一直苦於沒有下定決心去學習研究,最近開始接觸golang。就我我的來講,學習golang的原動力是由於想要站在java語言以外來審視java和其它語言的區別,再就是想瞻仰一下如此NB的語言。年前就想在2019年作一件事情,但願能從各個細節處作一次java和golang的對比分析,不評判語言的優劣,只想用簡單的語言和能夠隨時執行的代碼來表達出二者的區別和底層涉及到的原理。今天是情人節,饅頭媽媽在加班,送給本身一件貼心的禮物,寫下第一篇對比文章:java&golang的區別之:閉包。
關於閉包究竟是啥,建議參考知乎上的解釋: https://www.zhihu.com/question/51402215/answer/556617311

  • java8以前的閉包
在java8以前,java其實就已經對閉包有了必定層面的支持,實現的閉包方式主要是靠匿名類來實現的,下面是java程序員常常寫的一段代碼:
 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,也就是不能夠更改的(基礎類型不能夠更改,引用類型不能夠變動地址);多線程

3.被封裝的函數,不能夠調用上層方法所在對象的成員變量;
  • java8裏對閉包的支持

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,也就是不能夠更改的(基礎類型不能夠更改,引用類型不能夠變動地址);學習

3.lamda表達式,能夠調用和修改上層方法所在對象的成員變量;
因爲還沒時間分析jdk和hotspot的源碼,在此只能猜想推理,第2點和第3點的狀況。關於第2點:上層方法的局部變量必須是final修飾的,網上的文章大部分都是說由於多線程併發的緣由,沒法在lamda表達式裏進行修改上層方法的局部變量,這點上我是不一樣意這個觀點的。我認爲主要緣由是:java在定義局部變量時,對於基礎類型都是建立在stack frame上的,而一個方法執行完畢後,此方法所對應的stack frame也就沒有意義了,試想一下,lamda表達式所依賴的上層方法的局部變量的存儲區(stack frame)都消失了,咱們還怎麼可以修改這個變量,這是毫無心義的,在java裏也很難實現這一點,除非像golang一下,在特定狀況下,更改局部變量的存儲區域(在heap裏存儲)。關於第3點:實現起來就比較容易,就是在lamda表達式的對象裏,建立一個引用地址,地址指向原上層方法所在對象的堆存儲地址便可。
  • golang裏對閉包的支持

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對比分析的第一篇文章,因爲調研分析的時間有限,不免有疏忽之處,歡迎各位指正。

相關文章
相關標籤/搜索