烟台网站建站,阿里巴巴运营模式,北京网站开发团队,随州抖音seo收费标准异常处理
Go的设计者认为其它语言异常处理太过消耗资源#xff0c;且设计和处理复杂#xff0c;导致使用者不能很好的处理错误#xff0c;甚至觉得异常和错误处理起来麻烦而忽视、忽略掉#xff0c;从而导致程序崩溃。
为了解决这些问题#xff0c;Go将错误处理设…异常处理
Go的设计者认为其它语言异常处理太过消耗资源且设计和处理复杂导致使用者不能很好的处理错误甚至觉得异常和错误处理起来麻烦而忽视、忽略掉从而导致程序崩溃。
为了解决这些问题Go将错误处理设计的非常简单
函数调用返回值可以返回多值一般最后一个值可以是error接口类型的值 如果函数调用产生错误则这个值是一个error接口类型的错误如果函数调用成功则该值是nil 检查函数返回值中的错误是否是nil如果不是nil进行必要的错误处理
error是Go中声明的接口类型
type error interface {Error() string
}
//已经写入到go的源码中了所有实现 Error() string 签名的方法都可以实现错误接口。用Error()方法返回错误的具体描述。
自定义error
package mainimport fmt// 定义一个实现了error接口的自定义错误类型 errorString
type errorString struct {s string
}// 实现 errorString 的 Error 方法使其满足 error 接口
func (e *errorString) Error() string {return e.s
}
// New 函数用于创建并返回一个新的 errorString 实例接受一个字符串参数作为错误信息
func New(text string) error {return errorString{text}
}func main() {// 创建一个 errorString 类型的变量 e并初始化其值为 错误理由1var e errorString{错误理由1}// 打印 e 的值、通过 Error 方法获取错误信息、e 的地址以及通过指针调用 Error 方法获取错误信息fmt.Println(e, e.Error(), e, (e).Error())// 使用 New 函数创建一个错误错误信息为 错误理由2var err New(错误理由2)// 打印通过 Error 方法获取的错误信息fmt.Println(err.Error()) //e和err的区别
}在这个程序中通过定义一个实现了 error 接口的自定义类型 errorString并在其中实现了 Error 方法使其可以用作错误类型。然后通过 New 函数创建了两个错误实例打印访问这些错误实例的错误信息。e 变量
e 是直接创建的 errorString 类型的变量其值为 errorString{错误理由1}。
errorString 是一个自定义的类型实现了 error 接口。它包含一个字符串字段 s 和一个实现 Error 方法的函数。
因此e 的类型是 errorString但由于实现了 error 接口可以被赋值给 error 类型的变量。err 变量
err 是通过 New 函数创建的该函数返回一个 error 接口类型的指针指向一个 errorString 类型的实例。其值为 errorString{错误理由2}。
因为 New 函数返回的是 error 接口类型所以 err 的类型是 error 接口。
虽然底层实现是 errorString 类型但通过 error 接口只能访问 error 接口定义的方法即 Error 方法。
总体来说e 和 err 都代表实现了 error 接口的错误实例但 e 是直接的 errorString 类型的实例而 err 是通过 New 函数返回的 error 接口类型的指针指向 errorString 类型的实例。package mainimport (errorsfmt
)var ErrDivisionByZero errors.New(division by zero) // 构造一个错误实例因为在go的源码中已经写好了函数func New(text string) error {return errorString{text}}func div(a, b int) (int, error) {if b 0 {return 0, ErrDivisionByZero}return a / b, nil
}
func main() {if r, err : div(5, 0); err ! nil {fmt.Println(err.Error())} else {fmt.Println(r)}
}division by zeroErrDivisionByZero 是一个全局变量它是通过errors.New函数创建的一个错误实例表示除零错误。div 函数用于执行除法操作如果除数为零则返回 ErrDivisionByZero 错误否则返回计算结果和 nil 表示没有错误。在 main 函数中调用 div 函数并根据返回的错误信息进行相应的处理如果发生除零错误则打印错误信息否则打印计算结果。panic
panic是不好的因为它发生时往往会造成程序崩溃、服务终止等后果所以没人希望它发生。但是如果在错误发生时不及时panic而终止程序运行继续运行程序恐怕造成更大的损失付出更加惨痛的代价。所以有时候panic导致的程序崩溃实际上可以及时止损只能两害相权取其轻。
panic虽然不好体验很差但也是万不得已可以马上暴露问题及时发现和纠正问题。
panic产生
runtime运行时错误导致抛出panic比如数组越界、除零主动手动调用panic(reason)这个reason可以是任意类型
panic执行
逆序执行当前已经注册过的goroutine的defer链recover从这里介入打印错误信息和调用堆栈调用exit(2)结束整个进程
package mainimport fmtfunc div(a, b int) int {defer fmt.Println(1, start)defer fmt.Println(2, a, b)r : a / bfmt.Println(3, end)return r
}func main() {fmt.Println(div(5, 0))
}运行后程序崩溃因为除零异常输入如下
2 5 0
1 start
panic: runtime error: integer divide by zerogoroutine 1 [running]:
main.div(0x5, 0x0)f:/goprpject/test1/main.go:8 0x230
main.main()f:/goprpject/test1/main.go:14 0x25
exit status 2这是因为在r : a/b这一行已经panic了所以后面不执行了则defer从下往上输出recover
recover即恢复defer和recover结合起来在defer中调用recover来实现对错误的捕获和恢复让代码在发生panic后通过处理能够继续运行。类似其它语言中try/catch。
package mainimport (errorsfmtruntime
)// 创建一个全局变量 ErrDivisionByZero表示除零异常
var ErrDivisionByZero errors.New(除零异常)// 定义一个除法函数接受两个整数参数执行除法操作同时可能会引发panic异常
func div(a, b int) int {// 使用defer延迟执行函数用于处理panic异常defer func() {// 使用recover捕获panic异常并在defer中进行处理err : recover()fmt.Printf(1 %v, %[1]T\n, err)}()// 在defer中输出startdefer fmt.Println(start)// 在defer中输出a和b的值defer fmt.Println(a, b)// 在defer中进行处理panic异常的代码块defer func() {// 再次使用recover捕获panic异常err : recover()fmt.Printf(2 %v, %[1]T\n, err)// 使用类型断言判断panic异常的类型并根据类型进行不同的处理switch v : err.(type) {case runtime.Error:// 判断是否为runtime.Error类型如果是输出具体的原因fmt.Printf(原因%T, %#[1]v\n, v)case []int:// 判断是否为切片类型如果是输出原因切片fmt.Println(原因切片)}fmt.Println(离开recover处理)}()// 执行正常的除法操作r : a / b// 引发panic异常同时传递一个切片作为panic的值panic([]int{1, 3, 5})// 这里的代码不会被执行因为上面的panic会导致函数中断fmt.Println(end)// 返回计算结果return r
}func main() {// 调用除法函数可能引发panic异常fmt.Println(div(5, 0), !!!)// 在主函数中输出main exitfmt.Println(main exit)
}2 runtime error: integer divide by zero, runtime.errorString
原因runtime.errorString, integer divide by zero
离开recover处理
5 0
start
1 nil, nil
0 !!!
main exit
上例中一旦在某函数中panic当前函数panic之后的语句将不再执行开始执行defer。如果在defer中错误被recover后就相当于当前函数产生的错误得到了处理。当前函数执行完defer当前函数退出执行程序还可以从当前函数之后继续执行。
可以观察到panic和recover有如下
有panic一路向外抛出但没有一处进行recover也就是说没有地方处理错误程序崩溃有painc有recover来捕获相当于错误被处理掉了当前函数defer执行完后退出当前函数从当前函数之后继续执行