Go語言開發(七)、Go語言錯誤處理
一、defer延遲函數
1、defer延遲函數簡介
defer在聲明時不會立即執行,而是在函數return後,再按照FILO(先進後出)的原則依次執行每一個defer,一般用於異常處理、釋放資源、清理數據、記錄日誌等。
每次defer語句執行時,defer修飾的函數的返回值和參數取值會照常進行計算和保存,但是defer修飾的函數不會執行。等到上一級函數返回前,會按照defer的聲明順序倒序執行全部defer的函數。defer所修飾函數的任何返回值都會被丟棄。
如果一個defer所修飾函數的值為nil,則defer的函數會在函數執行時panic(異常),而不會在defer語句執行時panic。defer所修飾函數的上一級函數即使拋出異常,defer所修飾函數也會被執行的,確保資源被合法釋放。
package main
import "fmt"
func deferTest(){
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
}
func main() {
deferTest()//3,2,1
}
2、defer延遲函數應用
A、簡化資源回收
mu.Lock()
defer mu.Unlock()
defer?有一定的開銷, 為了節省性能可以避免使用的defer?
B、捕獲panic異常
Go語言中,panic用於拋出異常,,recover用於捕獲異常。
recover只能在defer語句中使用,直接調用recover是無效的。
package main import "fmt" func deferRecover(){ defer func () { if r := recover(); r != nil { fmt.Println("recover") } }() fmt.Println("exception will be happen") panic("exception has happped.") fmt.Println("return normally") } func main() { deferRecover() }
C、修改返回值
defer可以用於在?return?後修改函數的返回值。
package main
import "fmt"
func deferReturn(a,b int)(sum int){
defer func(){
sum += 100
}()
sum = a + b
return sum
}
func main() {
sum := deferReturn(1,6)
fmt.Println(sum)//107
}
D、安全回收資源
func set(mu *sync.Mutex, arr []int, i, v int) {
mu.Lock()
defer mu.Unlock()
arr[i] = v
}
如果運行時拋出切片越界異常,可以保證mu.Unlock()被調用。
二、錯誤處理
1、錯誤處理簡介
Go語言通過內置的錯誤接口提供了簡單的錯誤處理機制。
error類型是一個接口類型,定義如下:
type error interface {
Error() string}
Golang中引入error接口類型作為錯誤處理的標準模式,如果函數要返回錯誤,則返回值類型列表中肯定包含error。
2、錯誤處理使用
package main
import (
"fmt"
"errors"
)
//定義一個DivideError類型
type DivideError struct {
dividee int
divider int
}
//實現error接口
func (err *DivideError) Error() error{
strFormat := `Cannot proceed, the divider is zero.
dividee: %d
divider: 0`
return errors.New(fmt.Sprintf(strFormat, err.dividee))
}
//定義除法運算
func divide(vardividee int, vardivider int)(result int, errmsg error){
if vardivider == 0{
divideErr := DivideError{
dividee:vardividee,
divider:vardivider,
}
errmsg = divideErr.Error()
return 0,errmsg
}else{
return vardividee/vardivider,nil
}
}
func main() {
//正常情況
if result, err := divide(100, 10); err != nil{
fmt.Println(err)
}else{
fmt.Println("100/10 = ", result)
}
//當被除數為零的時候會返回錯誤信息
if _, errorMsg := divide(100, 0); errorMsg != nil{
fmt.Println(errorMsg)
}
}
三、異常處理
1、異常處理簡介
Go使用panic()函數拋出異常,在defer語句中調用recover()函數捕獲異常。
func panic(interface{})//接受任意類型參數 無返回值
func recover() interface{}//可以返回任意類型 無參數
panic()是一個內置函數,可以中斷原有的控制流程,進入一個panic流程中。當函數F調用panic,函數F的執行被中斷,但F中的延遲函數(必須是在panic前的已加載的defer)會正常執行,然後F函數逐層向上返回,直到發生panic的goroutine中所有調用的函數返回,此時程序退出。異常可以直接調用panic產生,也可以由運行時錯誤產生,例如訪問越界的數組。
recover()是一個內置函數,可以讓進入panic流程中的goroutine恢復過來。recover僅在延遲函數中有效。在正常的執行過程中,調用recover會返回nil,並且沒有其它任何效果。如果當前的goroutine陷入panic,調用recover可以捕獲到panic的輸入值,並且恢復正常的執行。
一般情況下,recover()應該在一個使用defer關鍵字的函數中執行以有效截取錯誤處理流程。如果沒有在發生異常的goroutine中明確調用恢復過程(使用recover關鍵字),會導致goroutine所屬的進程打印異常信息後直接退出。
2、異常處理使用示例
package main
import (
"errors"
"fmt"
)
//定義一個DivideError類型
type DivideError struct {
dividee int
divider int
}
//實現error接口
func (err *DivideError) Error() error{
strFormat := `Cannot proceed, the divider is zero.
dividee: %d
divider: 0`
return errors.New(fmt.Sprintf(strFormat, err.dividee))
}
//定義除法運算
func divide(dividee int, divider int)(result int){
defer func() {
if r := recover();r != nil{
divideErr := DivideError{
dividee:dividee,
divider:divider,
}
fmt.Println(divideErr.Error())
}
}()
result = dividee/divider
return result
}
func main() {
a := divide(100,0)
fmt.Println(a)
}
Go語言開發(七)、Go語言錯誤處理