在java或php等不少面向對象的語言中, 異常處理是依靠throw、catch來進行的。在go語言中,panic和recover函數在做用層面分別對等throw和catch語句,固然也存在不一樣之處。php
從設計層面來看,panic和recover函數適用於那些真正的異常(例如整數除0),而throw catch finally機制經常被用來處理一些業務層面的自定義異常。所以在go語言中,panic和recover要慎用。java
上述兩種異常機制的使用中,在處理異常時控制流程
的走向也是類似的。數組
下面將分別舉例說明:函數
try catch finally機制設計
try{ throw new Exception(); } catch(Exception $e) { do something ... } finally { }
這種機制中,咱們把可能拋出異常的語句或拋出自定義異常的語句放置到try語句塊中,而在catch塊中,咱們將上述語句拋出的異常捕獲,針對不一樣的異常進行報警或log等處理。以後,控制流程進入到finally語句塊中。若沒有finally語句,控制流程將進入到catch以後的語句中。也就是說,在這種機制中,控制流程是轉移到同一層級中異常捕獲以後的語句中。code
panic recover defer機制對象
在go的異常機制中,panic能夠將原有的控制流程中斷,進入到一個"恐慌"流程。這種恐慌流程能夠顯式調用panic()函數產生或者由運行時錯誤產生(例如訪問越界的數組下標)。panic會在調用它的函數中向本層和它的全部上層逐級拋出,若一直沒有recover將其捕獲,程序退出後會產生crash;若在某層defer語句中被recover捕獲,控制流程將進入到recover以後的語句中。進程
/* example 1 */ package main import ( "fmt" ) func f() { defer func() { fmt.Println("b") if err := recover();err != nil { fmt.Println(err) } fmt.Println("d") }() fmt.Println("a") panic("a bug occur") fmt.Println("c") } func main() { f() fmt.Println("x") }
在上述舉例中,輸出結果爲:io
a b a bug occur d x
這說明,在f函數中拋出的panic被本身defer語句中的recover捕獲,而後控制流程進入到recover以後的語句中,即打印d、打印x,以後進程正常退出。import
/* example 2 */ package main import ( "fmt" ) func g() { defer func() { fmt.Println("b") if err := recover();err != nil { fmt.Println(err) } fmt.Println("d") }() f() fmt.Println("e") } func f() { fmt.Println("a") panic("a bug occur") fmt.Println("c") } func main() { g() fmt.Println("x") }
上述案例的輸出結果是:
a b a bug occur d x
進程經歷了這樣一個過程:f()中拋出panic,因爲自身沒有定義defer語句,panic被拋到g()中。g()的defer語句中定義了recover,捕獲panic後並執行完defer剩餘的語句,以後控制流程被轉交到main()函數中,直至進程結束。