~~~~ 作為一個初學者,看到新的東西,感覺記下來。
~~~~ defer,就是在退出所處的函數時,調用後面帶的函數,defer後面隻能跟函數調用。
~~~~ 直接上例子:
package main
import (
"fmt"
)
func printf_defer(_val int){
fmt.Println("printf_defer val = ", _val)
}
func test1(){
fmt.Println("------------------test1 start------------------")
var i_defer_num int = 0
fmt.Println("------------------", i_defer_num, "------------------")
defer printf_defer(0)
i_defer_num++
fmt.Println("------------------", i_defer_num, "------------------")
defer printf_defer(1)
i_defer_num++
fmt.Println("------------------", i_defer_num, "------------------")
defer printf_defer(2)
i_defer_num++
fmt.Println("------------------test1 finish------------------")
}
func test2(){
fmt.Println("------------------test2 start------------------")
var i_defer_num int = 0
fmt.Println("------------------", i_defer_num, "------------------")
defer printf_defer(i_defer_num)
i_defer_num++
fmt.Println("------------------", i_defer_num, "------------------")
defer printf_defer(i_defer_num)
i_defer_num++
fmt.Println("------------------", i_defer_num, "------------------")
defer printf_defer(i_defer_num)
i_defer_num++
fmt.Println("------------------test2 finish------------------")
}
func main(){
test1()
test2()
}
~~~~ 下面是直接結果:
[email protected]-F4U0002:/mnt/f/go/gopath/test/test2/test1$ go run main.go
------------------test2 start------------------
------------------ 0 ------------------
------------------ 1 ------------------
------------------ 2 ------------------
------------------test2 finish------------------
printf_defer val = 2
printf_defer val = 1
printf_defer val = 0
------------------test1 start------------------
------------------ 0 ------------------
------------------ 1 ------------------
------------------ 2 ------------------
------------------test1 finish------------------
printf_defer val = 2
printf_defer val = 1
printf_defer val = 0
[email protected]-F4U0002:/mnt/f/go/gopath/test/test2/test1$
~~~~ 看上面的例子,我們可以發現兩個特點,第一defer在所處函數域結束的時候,會調用defer後面的函數,第二,defer儲存函數的方式是棧的方式,最後一個調用defer的函數,在結束的時候,第一個調用。
~~~~ panic,是用來表示非常嚴重的不可恢複的錯誤的,可以了解為抛出異常,帶一個參數,會列印出所帶的參數。
~~~~ 例子:
package main
import (
"fmt"
)
func printf_defer(_val int){
fmt.Println("printf_defer val = ", _val)
}
func test1(){
fmt.Println("------------------test1 start------------------")
var i_defer_num int = 0
fmt.Println("------------------", i_defer_num, "------------------")
defer printf_defer(i_defer_num)
i_defer_num++
fmt.Println("------------------", i_defer_num, "------------------")
defer printf_defer(i_defer_num)
i_defer_num++
fmt.Println("------------------", i_defer_num, "------------------")
defer printf_defer(i_defer_num)
i_defer_num++
if(i_defer_num > 0){
panic(1)
}
fmt.Println("------------------", i_defer_num, "------------------")
defer printf_defer(i_defer_num)
i_defer_num++
fmt.Println("------------------test1 finish------------------")
}
func main(){
test1()
}
~~~~ 運作結果:
[email protected]:/mnt/f/go/gopath/test/test2/test2$ go run main.go
------------------test1 start------------------
------------------ 0 ------------------
------------------ 1 ------------------
------------------ 2 ------------------
printf_defer val = 2
printf_defer val = 1
printf_defer val = 0
panic: 1
goroutine 1 [running]:
main.test1()
/mnt/f/go/gopath/test/test2/test2/main.go:22 +0x31c
main.main()
/mnt/f/go/gopath/test/test2/test2/main.go:30 +0x20
exit status 2
[email protected]:/mnt/f/go/gopath/test/test2/test2$
~~~~ 可以發現,在panic之前的defer都執行了,而在panic之後的defer沒有執行,然後提示的錯誤,之處的出錯的代碼函數,而且列出了調用的地方,而且這個時候,我們的程式直接崩潰了,沒有在運作下去了。既然是異常,那麼就有捕獲的辦法,那就是recover。
~~~~ recover,在panic之後,可以捕獲到目前的panic(如果有的話),被捕獲到的panic就不會向上傳遞了,這樣程式就不會奔潰了,在panic執行之後,會先調用在panic之前的defer,然後在程式奔潰,而我們隻要在panic執行之後的defer中執行recover就可以處理掉panic,注意在recover處理了panic之後,程式不會回到panic的位置,而是會繼續defer之後退出函數,傳回上一級。
~~~~ 例子:
package main
import (
"fmt"
)
func printf_defer(_val int){
fmt.Println("printf_defer val = ", _val)
}
func test1(){
defer func(){
fmt.Println("recovery start")
if err := recover(); err != nil {
fmt.Println("recovery panic")
fmt.Println(err)
}
fmt.Println("recovery finish")
}()
fmt.Println("------------------test1 start------------------")
var i_defer_num int = 0
fmt.Println("------------------", i_defer_num, "------------------")
defer printf_defer(i_defer_num)
i_defer_num++
fmt.Println("------------------", i_defer_num, "------------------")
defer printf_defer(i_defer_num)
i_defer_num++
fmt.Println("------------------", i_defer_num, "------------------")
defer printf_defer(i_defer_num)
i_defer_num++
if(i_defer_num > 0){
panic(1)
}
fmt.Println("------------------", i_defer_num, "------------------")
defer printf_defer(i_defer_num)
i_defer_num++
fmt.Println("------------------test1 finish------------------")
}
func main(){
fmt.Println("------------------main start------------------")
test1()
fmt.Println("------------------main finish------------------")
}
~~~~
[email protected]:/mnt/f/go/gopath/test/test2/test3$ go run main.go
------------------main start------------------
------------------test1 start------------------
------------------ 0 ------------------
------------------ 1 ------------------
------------------ 2 ------------------
printf_defer val = 2
printf_defer val = 1
printf_defer val = 0
recovery start
recovery panic
1
recovery finish
------------------main finish------------------
[email protected]:/mnt/f/go/gopath/test/test2/test3$
~~~~ 很好我們可以看見panic被處理掉了,程式直接到了上一級,程式運作沒有被中斷。