Go的defer和recover
在 Go 语言中,defer
和 recover
是两个紧密相关的关键字,主要用于错误处理和资源清理。它们通常一起使用,特别是在处理panic(运行时崩溃)时,确保程序不会直接崩溃,而是能够优雅地恢复并继续执行。
1. defer
关键字
作用
defer
用于延迟执行一个函数调用,通常用于:
- 资源释放(如关闭文件、数据库连接、解锁等)。
- 确保某些操作在函数返回前执行(即使函数提前返回或发生 panic)。
特点
- 延迟执行:
defer
语句不会立即执行,而是等到包含它的函数返回前才执行。 - 后进先出(LIFO):如果有多个
defer
,它们会按照逆序执行(类似栈结构)。 - 即使 panic 也会执行:
defer
语句在函数 panic 时仍然会执行,这使得它非常适合用于错误恢复。
示例
package mainimport "fmt"func main() {defer fmt.Println("1") // 最后执行defer fmt.Println("2") // 第二个执行fmt.Println("3") // 最先执行
}
输出:
3
2
1
说明:
defer
语句是延迟执行的,所以fmt.Println("3")
最先执行。- 两个
defer
按照逆序执行,所以2
在1
之前打印。
2. recover
关键字
作用
recover
用于捕获 panic,防止程序直接崩溃,并允许程序继续执行。
特点
- 只能在
defer
函数中使用:recover
必须在defer
调用的函数中使用,否则无效。 - 捕获 panic:如果程序发生 panic,
recover
可以捕获它,并返回 panic 传递的值。 - 不会终止程序:如果
recover
成功捕获 panic,程序不会崩溃,而是继续执行。
示例
package mainimport "fmt"func main() {defer func() {if r := recover(); r != nil { // 捕获 panicfmt.Println("捕获到 panic:", r)}}()panic("发生了一个严重错误!") // 触发 panicfmt.Println("这行不会执行") // 不会执行
}
输出:
捕获到 panic: 发生了一个严重错误!
说明:
panic
会导致程序崩溃,但defer
中的recover
捕获了它,程序不会退出。fmt.Println("这行不会执行")
不会执行,因为panic
已经发生,但recover
阻止了程序崩溃。
3. defer
+ recover
组合使用
典型场景
- 防止 panic 导致程序崩溃(如 HTTP 服务器、数据库操作等)。
- 资源清理(如关闭文件、解锁等),即使发生 panic 也要确保资源释放。
示例:防止 panic 崩溃
package mainimport "fmt"func safeDivide(a, b int) (result int) {defer func() {if r := recover(); r != nil { // 捕获 panicfmt.Println("捕获到 panic:", r)result = 0 // 返回默认值}}()return a / b // 如果 b=0,会 panic
}func main() {fmt.Println(safeDivide(10, 2)) // 正常情况fmt.Println(safeDivide(10, 0)) // 除零 panic
}
输出:
5
捕获到 panic: runtime error: integer divide by zero
0
说明:
safeDivide
函数在defer
中使用recover
捕获 panic。- 如果
b=0
导致 panic,recover
会捕获它,并返回0
而不是让程序崩溃。
示例:文件操作(确保文件关闭)
package mainimport ("fmt""os"
)func readFile(filename string) {file, err := os.Open(filename)if err != nil {fmt.Println("打开文件失败:", err)return}defer file.Close() // 确保文件关闭,即使发生 panic// 模拟 panicpanic("读取文件时发生错误!")// 正常情况下读取文件内容// buf := make([]byte, 1024)// file.Read(buf)// fmt.Println(string(buf))
}func main() {readFile("example.txt")fmt.Println("程序继续执行...")
}
输出:
打开文件失败: open example.txt: no such file or directory
程序继续执行...
说明:
- 即使
panic
发生,defer file.Close()
仍然会执行,确保文件被关闭。 - 程序不会崩溃,而是继续执行
fmt.Println("程序继续执行...")
。
4. 总结
关键字 | 作用 | 特点 |
| 延迟执行函数调用 | 后进先出(LIFO),即使 panic 也会执行 |
| 捕获 panic | 只能在 中使用,防止程序崩溃 |
| 错误恢复 | 确保资源释放,防止 panic 导致程序崩溃 |
最佳实践
- 资源清理(如文件、数据库连接、锁)→
defer
。 - 防止 panic 崩溃(如 HTTP 服务器、关键计算)→
defer
+recover
。 - 避免滥用
recover
:recover
应该只用于预期内的 panic,而不是掩盖所有错误(如应该用error
返回值处理的错误)。
这样,你就可以在 Go 中优雅地处理错误和资源管理了! 🚀