Go语言异常处理
- 异常处理
- error接口
- panic
- recover
- 延时调用错误问题
异常处理
error接口
Go语言引入了一个关于错误处理的标准模式 它是Go语言内建的接口类型 它的定义如下
type error interface {
Error() string
}
Go语言的标准库代码包errors为用户提供了以下方法
package errors
type errorString struct {
text string
}
func New(text string) error {
return &errorString{text}
}
func (e *errorString) Error() string {
return e.text
}
另一个可以生成 error 类型值的方法是调用 fmt 包中的 Errorf 函数:
package fmt
import "errors"
func Errorf(format string, args ...interface{}) error {
return errors.New(Sprintf(format, args...))
}
下面是代码示例
import (
"errors"
"fmt"
)
func main() {
var err1 error = errors.New("a normal err1")
fmt.Println(err1) //a normal err1
var err2 error = fmt.Errorf("%s", "a normal err2")
fmt.Println(err2) //a normal err2
}
一般来说 错误处理位于函数的最后 下面是代码示例
import (
"errors"
"fmt"
)
func Divide(a, b float64) (result float64, err error) {
if b == 0 {
result = 0.0
err = errors.New("runtime error: divide by zero")
return
}
result = a / b
err = nil
return
}
func main() {
r, err := Divide(10.0, 0)
if err != nil {
fmt.Println(err) //错误处理 runtime error: divide by zero
} else {
fmt.Println(r) // 使用返回值
}
}
panic
通常情况下 我们报错时可以返回一个error值
但是如果我们遇到了不可恢复的异常 比如说数组越界访问 空指针引用等
此时就会引发panic异常
一般而言 当当panic异常发生的时候 该程序会立刻停止运行 并且立即执行在该goroutine(我们这里暂时理解为线程)下的defer的函数
随后 程序崩溃并输出日志信息 日志信息包括panic value和函数调用的堆栈跟踪信息
此外需要注意的是 并不是所有panic异常都来自运行时 我们主动调用panic函数也会引起panic异常
panic函数可以接受任何值作为参数
func panic(v interface{})
下面是代码和结果演示
package main
import "fmt"
func testA() {
fmt.Println("testA ~~~")
}
func testB() {
panic("panic error ~~~")
}
func testC() {
fmt.Println("testC ~~~")
}
func main() {
testA()
testB()
testC()
}
我们发现前面的函数顺序执行完毕之后到panic异常
当然如果我们自己主动写点bug比如说数组越界 除0错误 效果也是一样的
recover
程序运行时 只要引发了panic异常就会引发程序崩溃 这是我们怎么都不愿意看到的
因此Go语言给我们提供了一种专门拦截运行时异常的内建函数 – recover
它可以使程序从panic状态恢复并获得流程控制权
注意:!!! recover只有在defer调用的函数中有效
如果说调用了内置函数decover并且定义该defer语句的函数发生了panic异常 recover会从panic中恢复 并且返回panic和vlaue 导致panic异常的函数不会正常运行 但会正常返回
但是在未发生panic时调用recover recover会返回nil
代码演示如下
package main
import "fmt"
func testA() {
fmt.Println("testA ~~~")
}
func testB() (err error) {
defer func() {
if x := recover(); x != nil {
// 此时panic和value被添加到x中
err = fmt.Errorf("internal error: %v", x)
}
}()
panic("panic error ~~~")
return err
}
func testC() {
fmt.Println("testC ~~~")
}
func main() {
testA()
err := testB()
fmt.Println(err)
testC()
}
我们依旧是在test2中主动调用了panic异常
只不过我们在前面延时调用了一个匿名函数
defer func() {
if x := recover(); x != nil {
// 此时panic和value被添加到x中
err = fmt.Errorf("internal error: %v", x)
}
}()
在这个匿名函数中我们调用了recover函数 并且接受了err
最终我们可以发现我们的程序正常运行下去了
延时调用错误问题
如果我们在延时调用中也引发了panic异常 那么该panic异常可以被后续延时调用的recover捕获
代码和结果演示如下
func test() {
defer func() {
fmt.Println(recover())
}()
defer func() {
panic("test2")
}()
panic("test1")
}
func main() {
test()
}
此时我们的recover捕获的错误就不是test1 而是 test2