Go語言入門-異常處理-pannic和recover

Go語言入門-異常處理-pannic和recover

簡述

Go語言try…catch結構化的異常機制,但是可以使用panic進行錯誤的拋出,使用recover來進行異常恢復處理。

基礎語法

func panic(interface{})
func recover() interface{}

使用場景

Go語言函數中可以拋出一個運行時恐慌後,然後通過defer使用recover來處理運行時恐慌。panic和recover是內置函數。而不是語句。由於panic參數是空接口類型,因此可使用任何類型作爲錯誤狀態。而recover返回的結果同樣要做轉型才能獲取具體信息。
來個例子吧

  • 示例1
    panic中斷了當前main函數執行
func main() {
    panic("I will deal")
}
/**
ouput:
goroutine 1 [running]:
main.main()
	~/panic.go:4 +0x40
*/
  • 示例2
    panic中斷了當前main函數執行,然後使用recover進行異常恢復處理–錯誤示範
func main() {
    panic("I will deal")
    defer func() {
        fmt.Println(recover())
    }()
}
/**
ouput:
goroutine 1 [running]:
main.main()
	~/panic.go:4 +0x40
*/

哎爲 啥執行還是異常了,不是說把recover放在defer裏面就可以了嗎?不好意思除了這個限制外還有一個限制:

defer 必須放在 panic 之前定義

  • 示例3
    panic中斷了當前main函數執行,然後使用recover進行異常恢復處理–正確示範
func main() {
    //defer放在panic前面
    defer func() {
        fmt.Println(recover())
    }()
    //我要panic了
    panic("I will deal")
}
/**
output:
I will deal
 */
  • 示例4
    多次panic演示,多次panic只捕獲第一panic的,不會recover以後又跑搭配panic後續的處理邏輯。

那麼是不是在一個函數中只能引發以後panic呢 ?

func main() {
    defer func() {
        fmt.Println(recover())
    }()
    
    panic("I will deal1") //只會執行一次因爲當前panic已經中斷掉main,被defer函數捕獲後直接退出main函數不在執行panic後續處理邏輯
    panic("I will deal2")
}
/**
output:
I will deal1
 */
  • 示例5
    在defer語句中panic的場景,覆蓋掉普通函數中的panic
func main() {
    defer func() {
        fmt.Println(recover())//recover的竟然是defer語句的panic?
    }()
    defer func() {
        panic("I will deal3") 
    }()
    panic("I will deal1") //此行代碼是否執行了?
    panic("I will deal2")
}
/**
output:
I will deal3
 */

以上案例中發現recover捕獲的最後執行的pannic,按照程序執行的邏輯,panic(“I will deal1”)應該是執行後出發defer語句中的 panic(“I will deal3”) 然後最後一個defer
recover捕獲了defer語句的中panic

意味着panic在一個函數中是可以執行多次,但是在正常的處理邏輯只會執行一次,當defer語句引發panic後,普通函數中的panic會被覆蓋。因此可以再defer語句先恢復recover 運行時恐慌panic再次拋出運行時恐慌。

  • 示例6
    在defer語句中panic的場景中先恢復panic再拋出panic
func main() {
    defer func() {
        fmt.Println(recover())//恢復最後一次panic
    }()
    defer func() {
        fmt.Println(recover()) //恢復普通函數的panic
        panic("I will deal3")
    }()
    panic("I will deal1") //此行代碼是否執行了?
    panic("I will deal2")
}
/**
output:
I will deal1
I will deal3

 */

以上例子中可以看出 panic可以多次執行,但是通過實例5的比較可以得出以下結論。

當多次panic拋出以後如果沒有recover掉,panic會發生覆蓋,最後recover如果恢復或者捕獲的話,只會捕獲到最後一個沒有被捕獲的panic。

  • 示例7
    recover的使用範圍,只能放在defer延遲調用的函數中直接調用,其他花裏胡哨的寫法都只能拿到nil。

func testRecover() {
    defer fmt.Println("defer ", recover())
    fmt.Println("test recover 1", recover())
    defer func() {
        func() {
            fmt.Println("defer func inner 1", recover())
        }()
        //捕獲recover
        fmt.Println("defer func ", recover())
        func() {
            fmt.Println("defer func inner 2", recover())
        }()
    }()
    defer fmt.Println(recover())
    fmt.Println("test recover 2", recover())
    panic("call panic")
    fmt.Println("test recover 3", recover())
}
func main() {
    testRecover()
}
/**
output:
test recover 1 <nil>
test recover 2 <nil>
<nil>
defer func inner 1 <nil>
defer func  call panic
defer func inner 2 <nil>
defer  <nil>
 */

1. recover必須在defer調用中直接使用
2. defer必須要在pannic之前

  • 示例8
    panic捕獲後繼續處理程序(保護代碼段)
func recover1( a, b int) {
    result := 0
    //result := 19/0
    //fmt.Println(result)
    //.\panic.go:121:17: division by zero
    func() {
        defer func() {
            if recover() != nil {
                result = 0
                fmt.Println("ReSet result=[0]")
            }
        }()
        if result == 0 {
            panic("division by zero")
        }
        result = a/b
        fmt.Println(result)
    }()
}

func main() {
    recover1(10, 0)
}
/**
output:
ReSet result=[0]
 */

使用匿名函數的作用域用來保證某一段代碼能夠一直順序執行,保證函數不被中斷掉。

  • 示例9
    模擬try…catch todo
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章