错误、异常处理
十九、错误、异常处理
本章吐槽:go的异常处理实在是太难用了,一度让我写得十分反胃。
1 error
go中我们通常会在函数或方法中返回error
结构对象来判断是否有异常出现。go内置的错误类型error
是一个接口类型,自定义的错误类型必须实现它:
type error interface {
Error() string
}
使用errors.New
可返回一个错误信息:
err := errors.New("错误消息")
例:
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("被开方数不能为负")
}
return math.Sqrt(f), nil
}
func main() {
d, err := Sqrt(-3)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(d)
}
在上面代码中,我们定义一个简单的开方函数,如果被开方数为负就输出一个错误。
一般按照如下方法使用:
- 如果函数需要处理异常,通常将error作为多值返回的最后一个值,返回的error值为nil则表示无异常,非nil则是有异常。
- 一般先用if语句处理
error!=nil
,把正常逻辑写在后面。
另外你会发现,返回错误并不会终止程序的运行。
2 panic
error是错误,panic是异常。错误指的是可能出现问题的地方出现了问题,比如打开一个文件时失败,这种情况在人们的意料之中 ;而异常指的是不应该出现问题的地方出现了问题,比如引用了空指针,这种情况在人们的意料之外。可见,错误是业务过程的一部分,而异常不是 。与error不同,panic会终止程序的运行,在执行完所有的defer函数后,程序控制返回到该函数的调用方。这样的过程会一直持续下去,直到当前goroutine的所有函数都返回退出,然后程序会打印出 panic 信息,接着打印出堆栈跟踪(Stack Trace),最后程序终止。
func main() {
fmt.Println("1")
fmt.Println("2")
panic("发生异常") // 主动抛异常,类似于java的throw,python的raise
fmt.Println("3")
}
上述代码中,使用panic
抛出异常后,第三个打印语句不会执行。
3 defer
defer是go语言中的延迟执行语句,用来添加函数结束时执行的代码。
func main() {
defer fmt.Println("1")
defer fmt.Println("2")
fmt.Println("3")
}
/*结果为:
3
2
1
*/
当一个函数内多次调用defer时,go会把defer调用放入到一个栈中,在函数结束之前依次出栈,因此输出结果是逆序。
与panic结合使用:
func main() {
defer fmt.Println("1")
defer fmt.Println("2")
panic("发生异常") // 主动抛异常,类似于java的throw,python的raise
fmt.Println("3")
}
在抛出异常之前,再逆序执行所有的defer。
4 recover
panic会使后面的语句终止,recover可以让程序能正常运行下去。只有在defer函数的内部,调用 recover才有用。在defer函数内调用 recover,可以取到 panic的错误信息,并且停止 panic 续发事件(Panicking Sequence),程序运行恢复正常。recover的使用如下:
func f1() {
fmt.Println("f1")
}
func f2() {
defer func() { // 匿名函数,延迟执行
if err := recover(); err != nil {
fmt.Println(err) // 如果出错,恢复错误继续执行,并且打印错误
}
fmt.Println("无论是否有异常,此处永远都会执行")
}()
fmt.Println("f2")
panic("抛出异常")
}
func main() {
f1()
f2()
}
本文来自博客园,作者:yyyz,转载请注明原文链接:https://www.cnblogs.com/yyyzyyyz/p/15685640.html