defer
- go语言是实用defer做资源处理的
- defer是在函数结束,return之前时被调用
- 先进后出,可见是栈的结构
- 参数在defer语句时计算
何时使用defer
- open/close
- lock/unlock
- printHeader/printFooter
func calc(index string, a, b int) int {
ret := a + b
fmt.Printf("%s %d + %d = %d\n",index, a, b, ret)
return ret
}
func main() {
a := 1
b := 2
defer calc("first defer:", a, calc("first inner:", a, b))
a = 3
defer calc("second defer:", a, calc("second inner:", a, b))
b = 4
}
//结果
//first inner: 1 + 2 = 3
//second inner: 3 + 2 = 5
//second defer: 3 + 5 = 8
//first defer: 1 + 3 = 4
- 根据defer的执行顺序, first defer肯定是最后执行的
- first defer虽然最后执行,但是其参数first inner却是第一个执行
- first defer执行时的参数状态,保留的是其位置之前的,比如a后来修改成了3对其没有任何影响
panit/recover
panic
- 停止当前函数执行
- 一直向上返回,执行每一层的defer
- 如果没有遇见recover,程序退出
recover
- 仅在defer调用中使用,程序运行中间是没法调用recover的
- defer的时候,获取panic的值
- 如果无法处理,可重新panic
func tryRecover(){
defer func() {
r := recover()
// r是一个interface接口类型
if err, ok := r.(error); ok{
fmt.Println("error occurred", err)
} else{
panic(r)
}
}()
panic(errors.New("this is a error"))
panic("this is a error")
}
func main(){
tryRecover()
}
- 如果是第一句panic ,那参数是error类型,recover获得的也是error类型,则打印出结果为: error occurred : this is a error
- 如果是第二句panic,那recover获得的就是string类型,打印出来就是栈的异常信息
- 但是如果把err, ok := r.(error)改为err, ok := r.(string),则正常打印 error occurred : this is a error
实例:
func tryRecover(){
fmt.Println("tryRecover 1...")
defer func() {
r := recover()
if err, ok := r.(error); ok{
fmt.Println("error occurred: ", err)
} else{
panic(r)
}
}()
cala()
fmt.Println("tryRecover 3...")
}
func cala(){
fmt.Println("cala 1...")
b := 0
a := 1/b
fmt.Println(a)
fmt.Println("cala 2...")
}
func main(){
fmt.Println("start ...")
tryRecover()
fmt.Println("end ...")
}
输出结果:
start ...
tryRecover 1...
error occurred: runtime error: integer divide by zero
end ...
- 如果一个方法有异常(不管是来自于自己还是来自于调用方法),而且这个方法里没有recover的话,他会直接退出的,并且讲错误信息返回给上一层的。
- 如果一个方法有异常(不管是来自于自己还是来自于调用方法),且有recover,那么异常语句其后面的代码都不会执行了,直接进入recover,如果recover不再次panic,上一层正常执行下去
比如:
func tryRecover(){
fmt.Println("tryRecover 1...")
cala()
fmt.Println("tryRecover 3...")
}
func cala(){
fmt.Println("cala 1...")
defer func() {
r := recover()
if err, ok := r.(error); ok{
fmt.Println("error occurred: ", err)
} else{
panic(r)
}
}()
b := 0
a := 1/b
fmt.Println(a)
fmt.Println("cala 2...")
}
func main(){
fmt.Println("start ...")
tryRecover()
fmt.Println("end ...")
}
结果是:
start ...
tryRecover 1...
cala 1...
error occurred: runtime error: integer divide by zero
tryRecover 3...
end ...