Go語言學習[10]_異常處理
Go語言學習
道法自然文章目錄
前言
Go(又稱Golang)是Google開發的一種靜態強型別、編譯型、併發型,並具有垃圾回收功能的程式語言。Go語言語法簡單,包含了類C語法。快速的編譯時間,開發效率和執行效率高。組合的思想、無侵入式的介面。
一、異常處理
1.1 error
Go語言內建了一個簡單的錯誤介面作為一種錯誤處理機制,介面定義如下:
type error interface {
Error() string
}
它包含一個 Error() 方法,返回值為string
Go的error構造有兩種方式,分別是
第一種:errors.New()
err := errors.New("This is an error")
if err != nil {
fmt.Print(err)
}
第二種:fmt.Errorf()
err := fmt.Errorf("This is an error")
if err != nil {
fmt.Print(err)
}
除了直接使用Go自帶的方法,還可以自定義錯誤。下面以自然數函式作為例子:
type NotNature float64
func (err NotNature) Error() string {
return fmt.Sprintf("自然數為大於或等於0的數: %v", float64(err))
}
func Nature(x float64) (float64,error) {
if x<0 {
return 0,NotNature(x)
} else {
return x,nil
}
}
func main() {
fmt.Println(Nature(1))
fmt.Println(Nature(-1))
}
需要注意一下幾點:
1.如果函式需要處理異常,通常將error作為多值返回的最後一個值,返回的error值為nil則表示無異常,非nil則是有異常。
2.一般先用if語句處理error!=nil,正常邏輯放if後面。
Go語言的error代表的並不是真“異常”,只是通過返回error來表示錯誤資訊,換句話說,不是執行時錯誤範圍預定義的錯誤,某種不符合期望的行為並不會導致程式無法執行(自然數函式例子),都應使用error進行異常處理。當程式出現重大錯誤,如陣列越界,才會將其當成真正的異常,並用panic來處理。
1.2 panic
Go不使用try…catch方法來處理異常,而是使用panic和recover
先上程式碼舉一個簡單的例子
func main() {
fmt.Println("Hello,Go!")
panic(errors.New(" i am a error"))
fmt.Println("hello,again!")
}
輸出:
Hello,Go!
panic: i am a error
goroutine 1 [running]:
main.main()
~/error.go:12 +0xb5
exit status 2
可以看到,panic後面的程式不會被執行了。但是我們捕捉異常並不是為了停止程式(一般情況),而是為了讓程式能正常執行下去,這時候就到recover出場了。
可以看到,panic後面的程式不會被執行了。但是我們捕捉異常並不是為了停止程式(一般情況),而是為了讓程式能正常執行下去,這時候就到recover出場了。
package main
import "fmt"
func main(){
defer func(){
fmt.Println("我是defer裡面第一個列印函式")
if err:=recover();err!=nil{
fmt.Println(err)
}
fmt.Println("我是defer裡面第二個列印函式")
}()
f()
}
func f(){
fmt.Println("1")
panic("我是panic")
fmt.Println("2")
}
輸出:
1
我是defer裡面第一個列印函式
我是panic
我是defer裡面第二個列印函式
可以看到,f函式一開始正常列印,當遇到panic,就跳到defer函式,執行defer函式裡的內容
需要注意的是,defer函式裡列印的err其實就是panic裡面的內容。
下面詳細介紹一下panic和recover的原理。首先來看一下panic和recover的官方定義
func panic(v interface{})
內建函式panic會停止當前goroutine的正常執行。當函式F呼叫panic時,F的正常執行立即停止。任何被F延遲執行的函式都將以正常的方式執行,然後F返回其呼叫者。對呼叫方G來說,對F的呼叫就像呼叫panic一樣,終止G的執行並執行任何延遲的函式。直到執行goroutine中的所有函式都按逆序停止。此時,程式將以非0退出程式碼終止。此終止序列稱為panicking,可由內建函式recover控制。
func recover() interface{}
recover內建函式允許程式管理panicking的goroutine的行為。在defer函式(但不是它呼叫的任何函式)內執行恢復呼叫,通過恢復正常執行來停止panicking序列,並檢索傳遞給panic呼叫的錯誤值。如果在defer函式之外呼叫recover,則不會停止panicking的序列。在這種情況下,或者當goroutine不panicking時,或者提供給panic的引數是nil,recover返回nil。因此,recover的返回值報告goroutine是否panicking
ps:defer和recover必須在panic之前定義,否則無效。
1.3 原始碼分析
errors.New的定義如下
// src/errors/errors.go
// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
return &errorString{text}
}
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
1.New函式返回格式為給定文字的錯誤
2.即使文字是相同的,每次對New的呼叫都會返回一個不同的錯誤值。