Go语言的错误处理设计思想中主张
1.如果一个函数可能出现异常,那么应该把异常作为返回值,没有异常就返回 nil
2.每次调用可能出现异常的函数时,都应该主动进行检查,并做出反应,这种 if 语句术语叫 卫述语句
所以异常应该总是掌握在我们的手上,保证每次操作产生的影响达到最小,保证程序即使部分地方出现问题,也不会影响整个程序的运行,及时的处理异常,这样就可以减轻上层处理异常的压力,同时也不要让未知的异常使你的程序崩溃
我们应该让异常以这样的形式出现
func Demo() (int, error)
我们应该让异常以这样的形式处理(卫述语句)
_,err := errorDemo()
if err!=nil{
fmt.Println(err)
return
}
下面我们来说下如何使用和定义异常处理
errors-根据 业务 自定义错误信息
Go 语言通过内置的错误接口提供了非常简单的错误处理机制
我们可以在编码中通过实现 error 接口类型来生成错误信息(go 内置的一些函数里面,可直接通过 err 获取错误信息)。
如果一个函数需要根据业务逻辑返回错误信息,可使用 errors.New 返回一个自定义的错误:
func Sqrt(f float64) (float64, error) { if f < 0 { return 0, errors.New("math: square root of negative number") } } func Test() result, err:= Sqrt(-1) //通过 err 获取方法出错时返回的 errors.New 的内容 if err != nil { fmt.Println(err.Error()) //打印异常信息 }
panic-系统中断异常
主要使用场景
1.发生严重错误必须让进行退出:Go 的类型系统会在编译时捕获很多异常,但有些异常只能在运行时检查,如数组访问越界、空指针引用等。这些运行时异常会引起 painc 异常(程序直接崩溃退出),这个时候哪怕是执行下去程序也是毫无意义的,这个时候 panic 暴露出问题反而是更可取的方式
2.快速退出错误处理:大多数情况下错误处理都应该使用判断 error 的机制,但是有时函数调用栈很深,逐层返回错误可能需要写很多冗余代码,这个时候可以使用 panic 让程序的控制流直接跳到顶层的 recover 来进行处理。这种场景需要注意必须在包内就要 recover。让 panic 垮包传递可能会导致更复杂的问题,所以包的到处函数不应该产生 panic
recover-捕获panic
recover 是一个内建的函数,用于重新获得 panic 协程的控制。 只有在延迟函数的内部(需要使用 defer 修饰),调用 recover 才有用
在延迟函数内调用 recover,可以取到 panic 的错误信息,并且停止 panic 续发事件(Panicking Sequence),程序运行恢复正常(目的是让程序不崩溃,进而可以将 panic 异常转为普通 error 异常进行写入日志或者返回错误信息等操作)。如果在延迟函数的外部调用 recover,就不能停止 panic 续发事件。
func main() { n := 0 val, err := errTest(n) if err != nil { //由于 panic 之前没有对 err 进行赋值,所以 err 为 nil fmt.Print(err.Error()) } fmt.Println(n) } func errTest(n int) (int, error) { //被 defer 修饰的函数不会马上执行,而是会等到该函数执行完成后才会执行,执行顺序为倒序 defer func() { //recover 会捕获当前函数的 panic 异常,如果有,则进行处理 if err := recover(); err != nil { fmt.Println("defer",err) } }() if n == 0 { //err=fmt.Errorf("errTest error") panic("panic error") //调用 panic 异常,当前函数直接退出,不做任何返回 } return n, nil }