KN001.defer,panic和recover
1.关键字说明
1.1 defer
- defer是go提供的一种资源处理的方式
- 处理 类似文件关闭、锁释放,连接关闭等等,类似于java的finally的方法,在方法体执行结束后执行的
- 执行 先进后出 的,与 return 搭配时,return 优先级高于 defer
1.2 panic和recover
- panic 和 recover 是 Go 收集程序发生异常的 机制
- 通俗理解的表达
- panic 前 必须定义 defer (换句话说,defer必须定义在panic之前)
- recover 必须定义在defer 内部,否负责无效
- panic 发生异常,如果没有 defer中的recover 接收处理,导致程序中断
- 优雅的表达
- defer 需要放在 panic 之前定义,另外 recover 只有在 defer 调用的函数中才有效
- recover 处理异常后,逻辑并不会恢复到panic那个点去,函数跑到 defer 之后的那个点
2.示例说明规则
2.1 基础热身
示例1
func defer_call() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("触发异常")
// 执行结果
// 打印后
// 打印中
// 打印前
// panic: 触发异常
}
示例2
func defer_call1() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("触发异常")
fmt.Println("panic 后面的代码段...")
// 执行结果
// 打印后
// 打印中
// 打印前
// panic: 触发异常
}
// 扩展 示例 ,验证 发生了 panic 后, 后面的 defer 代码亦将无法执行
func defer_call2() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("触发异常")
defer fmt.Println("panic 后面的代码段...")
// 执行结果
// 打印后
// 打印中
// 打印前
// panic: 触发异常
}
注意
panic发生异常,如果没有recover 捕获 ,程序将中断。
示例3
// defer 的用法的三个原则
// 1. 在defer表达式被运算的同时,defer函数的参数也会被运算
// 2. defer函数在一个函数return之后遵循后进先出的调用原则
// 3. defer函数可能读取并赋值给所在函数的返回值
func deferRule1() {
i := 0
defer fmt.Println(i)
i++
return
// 输出结果是
// 0
}
func deferRule2() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
// 输出结果是
// 3210
}
func deferRule3() (i int) {
defer func() {
i++
}()
return 1
// 执行结果
// i = 2
}
// 针对defer的第三点,存在3种情况
func deferRule3_1() (result int) {
defer func() {
result++ // result = result + 1 , result = 1
}()
return 0 // result = 0
// 执行结果
// 1
// 详细解说
/*
func deferRule3_1() (result int) {
result = 0 //return语句不是一条原子调用,return xxx其实是赋值+ret指令
func() { //defer被插入到return之前执行,也就是赋返回值和ret指令之间
result++
}()
return
}
*/
}
func deferRule3_2() (r int) {
t := 5
defer func() {
t = t + 5 // t = t + 5 , t = 10
}()
return t // t = 5 , r = t , r = 5
//执行结果
//5
// 详细解说
/*
func deferRule3_2() (r int) {
t := 5
r = t //赋值指令
func() { //defer被插入到赋值与返回之间执行,这个例子中返回值r没被修改过
t = t + 5
}
return //空的return指令
}
*/
}
func deferRule3_3() (r int) {
defer func(r int) {
r = r + 5 //r = r + 5 , r = 5
}(r) // r = 0 , r 值传递到defer 函数
return 1 // r = 1
// 执行解雇
// 1
// 详细解说
/*
func deferRule3_3() (r int) {
r = 1 //给返回值赋值
func(r int) { //这里改的r是传值传进去的r,不会改变要返回的那个r值
r = r + 5
}(r)
return //空的return
}
*/
}
示例4
// panic 和 recover的
func f() {
defer func() {
fmt.Println("defer start")
if err := recover(); err != nil {
fmt.Println(err) // 这里的err其实就是panic传入的内容,“bug”
}
fmt.Println("defer end")
}()
for {
fmt.Println("func begin")
a := []string{"a", "b"}
fmt.Println(a[3]) // 越界访问,肯定出现异常
panic("bug") // 上面已经出现异常了,所以肯定走不到这里了。
fmt.Println("func end") // 不会运行的.
time.Sleep(1 * time.Second)
}
//执行结果
// func begin
// defer start
// runtime error: index out of range [3] with length 2
// defer end
}
panic 和 recover延迟
// panic 和 recover的, 发生异常后,执行 recover 的 defer,然后在执行 defer栈的函数
func f1() {
defer fmt.Println("defer recover before ...")
defer func() {
fmt.Println("defer start")
if err := recover(); err != nil {
fmt.Println(err) // 这里的err其实就是panic传入的内容,“bug”
}
fmt.Println("defer end")
}()
defer fmt.Println("defer recover after ...")
for {
fmt.Println("func begin")
a := []string{"a", "b"}
fmt.Println(a[3]) // 越界访问,肯定出现异常
panic("bug") // 上面已经出现异常了,所以肯定走不到这里了。
fmt.Println("func end") // 不会运行的.
// time.Sleep(1 * time.Second)
}
fmt.Println("main end")
//执行结果
// func begin
// defer recover after ...
// defer start
// runtime error: index out of range [3] with length 2
// defer end
// defer recover before ...
}
// 如果存在多个recover的defer栈时,按照 defer栈的原则,先进后出原则,第一个 出栈的 defer 捕获 panic的异常后,后面的 recover的defer 不会再捕获到panic的异常
func f2() {
defer fmt.Println("defer recover before ...")
defer func() {
fmt.Println("defer start")
if err := recover(); err != nil {
fmt.Println("defer recover 1 ... ", err)
}
fmt.Println("defer end")
}()
defer func() {
if err := recover(); err != nil {
fmt.Println("defer recover 2 ...", err)
}
fmt.Println("defer recover after ...")
}()
for {
fmt.Println("func begin")
a := []string{"a", "b"}
fmt.Println(a[3]) // 越界访问,肯定出现异常
panic("bug") // 上面已经出现异常了,所以肯定走不到这里了。
fmt.Println("func end") // 不会运行的.
// time.Sleep(1 * time.Second)
}
fmt.Println("main end")
// func begin
// defer recover 2 ... runtime error: index out of range [3] with length 2 // 异常被 recover2 捕获
// defer recover after ...
// defer start
// defer end
// defer recover before ...
}
3.总结
- defer的执行顺序
- 多个defer的执行顺序,原则是 先进后出
- return执行 的优先级 高于 defer
- panic 和 recover
- defer 必须定义在 panic之前,recover 必须定义在defer内才有效
- recover 处理异常后,逻辑并不会恢复到panic那个点去,函数跑到 defer 之后的那个点
- 多个defer recover栈,首个出栈的defer recover 捕获异常,其他的 recover的defer 将捕获不到异常。

浙公网安备 33010602011771号