关于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 的处理机制如下:

  1. 当调用 panic(...) 后,当前函数停止执行后续代码。
  2. 开始执行所有已经压入 defer 栈的 defer 函数(按 LIFO 后进先出 顺序)。
  3. 如果某个 defer 函数中调用了 recover(),并且它在同一个 goroutine 中捕获到了 panic,则这个 panic 会被终止,程序继续运行。
  4. 但如果 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

最终结果:

  1. main() 函数正常退出。
  2. 程序不会崩溃。
  3. 但捕获的是第二个 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")
}
posted @ 2025-07-17 19:25  Charlie-Pang  阅读(75)  评论(0)    收藏  举报