在 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 中優雅地處理錯誤和資源管理了! 🚀