天天看點

go語言defer、panic、recover

     ~~~~     作為一個初學者,看到新的東西,感覺記下來。

     ~~~~     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被處理掉了,程式直接到了上一級,程式運作沒有被中斷。