一、今日題目
-
下面列舉的是recover()的幾種調用方式,哪些是正確的?
A.func A() { recover() panic(1) }
B.
func main() { defer recover() panic(1) }
C.
func main() { defer func() { recover() }() panic(1) }
D.
func main() { defer func() { defer func() { recover() }() }() panic(1) }
-
下面代碼會輸出什麼,請說明?
func main() { defer func() { fmt.Print(recover()) }() defer func() { defer fmt.Print(recover()) panic(1) }() defer recover() panic(2) }
二、答案:
- C
- 21
三、解析:
- recover() 必須在defer函數中直接調用纔有效。A、B和D這幾種情況的調用都是無效的:直接調用recover()、在defer()中直接調用recover()和defer()調用時多層嵌套。
- recover() 必須在defer()函數中調用纔有效,所以第9行代碼捕獲是無效的。在調用defer()時,便會計算函數的參數並壓入棧中,defer()的執行順序爲:先進後出。最後panic(2)會向上傳遞到第二個defer()中。所以在執行第6行代碼時,會捕獲panic(2);此後的panic(1),繼續向上傳遞會被上一層的recover()捕獲。
四、相關知識總結:
1. panic()函數
panic是用來表示非常嚴重的且不可恢復的錯誤的。在Go語言中這是個內置函數,接受一個interface{}類型的值(也就是任何值)作爲參數。panic的作用就是異常,不過Go中沒有try…catch,所以panic一般會導致程序掛掉(除非有recover)。
注意:即使函數執行的時候panic了,函數不往下走了,運行時並不是立刻向上傳遞panic,而是到defer那,等defer的東西都跑完了,panic再往上傳遞。
2.recover()函數
Go語言提供recover內置函數,用來捕獲拋出的異常panic。
需要注意的是:必須要先聲明defer,否則不能捕獲到panic異常。正如上面說到的 ,recover()必須再defer函數中直接調用纔有效。不使用defer,直接defer recover(),或者嵌套使用defer 函數調用都是無效的。
recover()捕獲異常的機制:一旦遇到panic,panic函數不會立刻就返回,而是邏輯走到defer那,然後我們就在defer這調用recover函數就會捕獲當前的panic(如果有的話),然後將defer執行完。但是recover之後邏輯不會再恢復到panic那個點去。函數會在defer之後返回。這時捕獲的panic就不會向上傳遞了,程序就可以正常運行了。
實例:
func main() {
defer func() {
fmt.Println("c")
fmt.Println(recover())
fmt.Println("d")
}()
f()
}
func f() {
fmt.Println("a")
panic(55)
fmt.Println("b")
fmt.Println("f")
}
輸出結果:
a
c
55
d