1. 程式人生 > 程式設計 >java 後臺開發中model與entity(實體類)的區別說明

java 後臺開發中model與entity(實體類)的區別說明

閉包(英語:Closure),又稱詞法閉包(Lexical Closure)或函式閉包(function closures),是引用了自由變數的函式。這個被引用的自由變數將和這個函式一同存在,即使已經離開了創造它的環境也不例外。
-- wikipedia

閉包包含兩部分:

  1. 函式本身
  2. 函式所引用的環境

Go 裡閉包的函式必須是匿名函式。

/**
閉包:是引用了自由變數的函式。這個被引用的自由變數和這個函式一同存在,即使已經離開了建立的環境
*/
func myFunc() func() int {
	foo := 0
	//myFunc 裡面的匿名函式可以訪問並且更新 myFunc 裡面的變數,這個變數的生命週期因為匿名函式的存在而延長。
	return func() int {
		foo++
		return foo
	}
}

func main() {
	bar := myFunc()
	value_1 := bar()
	value_2 := bar()
	println(value_1)
	println(value_2)
}

執行結果:

D:\GoWorkspace\src\polaris\main>go run closures.go
1
2

使用閉包實現斐波那契額

package main

/**
使用閉包實現斐波那契額
*/

func makeFibGen() func() int {
	f1 := 0
	f2 := 1
	return func() (fib int) {
		fib = f1
		f2, f1 = (f1 + f2), f2
		return fib
	}
}

func main() {
	gen := makeFibGen()
	for i := 0; i < 10; i++ {
		println(gen())
	}
}

Go 中匿名函式的實現

package main

/**
匿名函式的實現
*/
func myFunc1(message int) {
	println(message)
}

func main() {
	f := func(message int) {
		println(message)
	}
	f(0x100)
	myFunc1(0x100)
}

編譯:

D:\GoWorkspace\src\polaris\main>go build -gcflags "-N -l -m" -o test2
# _/D_/GoWorkspace/src/polaris/main
.\test2.go:11:7: func literal does not escape

然後我們通過 go 提供的反彙編工具,反編譯我們剛剛生成的 test 檔案。

D:\GoWorkspace\src\polaris\main>go tool objdump -s "main\.main" ./test2
TEXT main.main(SB) D:/GoWorkspace/src/polaris/main/test2.go
  test2.go:10           0x45ad00                65488b0c2528000000      MOVQ GS:0x28, CX
  test2.go:10           0x45ad09                488b8900000000          MOVQ 0(CX), CX
  test2.go:10           0x45ad10                483b6110                CMPQ 0x10(CX), SP
  test2.go:10           0x45ad14                7642                    JBE 0x45ad58
  test2.go:10           0x45ad16                4883ec18                SUBQ $0x18, SP
  test2.go:10           0x45ad1a                48896c2410              MOVQ BP, 0x10(SP)
  test2.go:10           0x45ad1f                488d6c2410              LEAQ 0x10(SP), BP
  test2.go:11           0x45ad24                488d159d000200          LEAQ go.func.*+77(SB), DX
  test2.go:11           0x45ad2b                4889542408              MOVQ DX, 0x8(SP)
  test2.go:14           0x45ad30                48c7042400010000        MOVQ $0x100, 0(SP)
  test2.go:14           0x45ad38                488b0589000200          MOVQ go.func.*+77(SB), AX
  test2.go:14           0x45ad3f                ffd0                    CALL AX
  test2.go:15           0x45ad41                48c7042400010000        MOVQ $0x100, 0(SP)
  test2.go:15           0x45ad49                e852ffffff              CALL main.myFunc1(SB)
  test2.go:16           0x45ad4e                488b6c2410              MOVQ 0x10(SP), BP
  test2.go:16           0x45ad53                4883c418                ADDQ $0x18, SP
  test2.go:16           0x45ad57                c3                      RET
  test2.go:10           0x45ad58                e81373ffff              CALL runtime.morestack_noctxt(SB)
  test2.go:10           0x45ad5d                eba1                    JMP main.main(SB)
  :-1                   0x45ad5f                cc                      INT $0x3

TEXT main.main.func1(SB) D:/GoWorkspace/src/polaris/main/test2.go
  test2.go:11           0x45ad60                65488b0c2528000000      MOVQ GS:0x28, CX
  test2.go:11           0x45ad69                488b8900000000          MOVQ 0(CX), CX
  test2.go:11           0x45ad70                483b6110                CMPQ 0x10(CX), SP
  test2.go:11           0x45ad74                7635                    JBE 0x45adab
  test2.go:11           0x45ad76                4883ec10                SUBQ $0x10, SP
  test2.go:11           0x45ad7a                48896c2408              MOVQ BP, 0x8(SP)
  test2.go:11           0x45ad7f                488d6c2408              LEAQ 0x8(SP), BP
  test2.go:12           0x45ad84                e84731fdff              CALL runtime.printlock(SB)
  test2.go:12           0x45ad89                488b442418              MOVQ 0x18(SP), AX
  test2.go:12           0x45ad8e                48890424                MOVQ AX, 0(SP)
  test2.go:12           0x45ad92                e80939fdff              CALL runtime.printint(SB)
  test2.go:12           0x45ad97                e8d433fdff              CALL runtime.printnl(SB)
  test2.go:12           0x45ad9c                e8bf31fdff              CALL runtime.printunlock(SB)
  test2.go:13           0x45ada1                488b6c2408              MOVQ 0x8(SP), BP
  test2.go:13           0x45ada6                4883c410                ADDQ $0x10, SP
  test2.go:13           0x45adaa                c3                      RET
  test2.go:11           0x45adab                e8c072ffff              CALL runtime.morestack_noctxt(SB)
  test2.go:11           0x45adb0                ebae                    JMP main.main.func1(SB)

一共有三次 CALL, 排除調最後那個 runtimeCALL ,剩下兩次分別對應了匿名函式呼叫以及正常的函式呼叫。而兩次的區別在於正常的函式是 CALL main.myFunc(SB) , 匿名函式的呼叫是 CALL BX 。這兩種不同的呼叫方式意味著什麼?我們可以通過 gdb 來動態的跟蹤這段程式碼來具體分析一下。

注意事項

  1. 匿名函式作為返回物件效能上要比正常的函式效能要差。
  2. 閉包可能會導致變數逃逸到堆上來延長變數的生命週期,給 GC 帶來壓力。

參考:
http://sunisdown.me/closures-in-go.html
https://gobyexample.com/closures