关于defer、panic、recover的总结
解释:
recover() 是 Go 中用于 恢复 panic 的内置函数。
它只能在 defer 调用的函数中使用。
如果当前程序没有发生 panic,recover() 返回 nil。
如果发生了 panic,它会返回传入 panic(...) 的值(如字符串、error、任意类型)。
func main() {
defer func() {
//err := recover()如果发生了panic,返回panic的值给err(比如下面返回了panic11,就会把panic11赋值给err),否则返回nil
//err != nil表示如果err不为nil,则说明发生了panic,此时会打印捕获到的异常信息
if err := recover(); err != nil {
fmt.Println("捕获了异常:", err)
}
}()
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
panic("panic11")
}
案例2:
这是一个非常经典的 Go 中 panic 和 defer 执行顺序的问题。
虽然看起来 panic("第一个 Panic") 是最先触发的,但由于 第二个 defer 函数又触发了一个 panic,导致第一个 panic 的信息 被覆盖了,最终 没有被捕获,程序会崩溃并打印堆栈信息。
为什么?
Go 中 panic 的处理机制如下:
- 当调用 panic(...) 后,当前函数停止执行后续代码。
- 开始执行所有已经压入 defer 栈的 defer 函数(按 LIFO 后进先出 顺序)。
- 如果某个 defer 函数中调用了 recover(),并且它在同一个 goroutine 中捕获到了 panic,则这个 panic 会被终止,程序继续运行。
- 但如果 defer 函数本身又调用了 panic,则原来的 panic 信息会被覆盖。
执行流程详解
我们来逐步分析你的代码执行顺序:
第一步:进入 main() 函数
注册两个 defer 函数(注意顺序):
- defer A: 匿名函数,用于 recover
- defer B: 匿名函数,里面调用了 panic("第二个 Panic")
Go 的 defer 是 后进先出(LIFO) 的,所以:
defer B 会比 defer A 更早执行。
第二步:执行 panic("第一个 Panic")
- 停止 main() 中后续代码执行。
- 开始执行 defer 函数,顺序如下:
先执行 defer B:
func() {
panic("第二个 Panic")
}()
- 又触发一个新的 panic,即 panic("第二个 Panic")
- 此时,Go 认为这是当前正在处理的 panic,原来的 "第一个 Panic" 被 丢弃 / 覆盖。
再执行 defer A
if err := recover(); err != nil {
fmt.Println("捕获了异常:", err)
}
此时 recover 捕获到的是 最后一个 panic,也就是 "第二个 Panic"。
最终输出:捕获了异常: 第二个 Panic
最终结果:
- main() 函数正常退出。
- 程序不会崩溃。
- 但捕获的是第二个 panic,不是第一个。
func main() {
defer func() {
//err := recover()如果发生了panic,返回panic的值给err(比如下面返回了panic11,就会把panic11赋值给err),否则返回nil
//err != nil表示如果err不为nil,则说明发生了panic,此时会打印捕获到的异常信息
if err := recover(); err != nil {
fmt.Println("捕获了异常:", err)
}
}()
defer func() {
panic("第二个 panic")
}()
panic("第一个 panic")
}

浙公网安备 33010602011771号