在 Go 语言中,defer 语句是一个超级有用且常用的特性,它允许推迟一个函数调用的执行,直到包含 defer 语句的函数即将返回时才执行。defer 常用于资源清理,如关闭文件、释放锁、关闭网络连接等。
defer functionCall()defer 后面跟一个函数调用(或方法调用),该调用会被推迟执行。
package main
import "fmt"
func main() {
defer fmt.Println("deferred print")
fmt.Println("normal print")
return
// 输出:
// normal print
// deferred print
}defer 语句在执行时会立即计算函数的参数,但函数本身被推迟执行。
package main
import "fmt"
func main() {
i := 10
defer fmt.Println(i) // 输出 10,不是 20
i = 20
fmt.Println("hello")
}
// 输出:
// hello
// 10注意:fmt.Println(i) 中的 i 在 defer 被执行时就被求值为 10,即使后面 i 被修改为 20。
如果有多个 defer,它们会以栈的方式执行:后声明的先执行。
package main
import "fmt"
func main() {
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
}
// 输出:
// 3
// 2
// 1package main
import (
"log"
"os"
)
func main() {
file, err := os.Open("data.txt")
if err != nil {
log.Fatal(err)
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
log.Fatal(err)
}
}(file) // 确保函数结束时关闭文件
}mu.Lock()
defer mu.Unlock()
// 临界区操作package main
import (
"fmt"
)
func main() {
b()
}
func trace(s string) string {
fmt.Println("进入:", s)
return s
}
func un(s string) {
fmt.Println("退出:", s)
}
func a() {
defer un(trace("a")) // trace("a") 立即执行,返回 "a",un("a") 推迟到后面
fmt.Println("in a")
}
func b() {
defer un(trace("b"))
fmt.Println("in b")
a()
}
输出:
进入: b
in b
进入: a
in a
退出: a
退出: bdefer 可以修改命名返回值(named return values)。
func c() (i int) {
defer func() {
i++ // 修改命名返回值
}()
i = 1
return i // 返回的是 i,此时 i 已经被 defer 改为 2
}// ❌ 错误示例:在循环中 defer 文件关闭
for _, file := range files {
f, _ := os.Open(file)
defer f.Close() // 所有文件都在函数结束时才关闭
}应改为:
// ✅ 正确做法:在函数内处理
for _, file := range files {
func() {
f, _ := os.Open(file)
defer f.Close()
// 处理文件
}()
}特性 | 说明 |
执行时机 | 函数 return 前 |
参数求值 | defer 时立即求值 |
执行顺序 | LIFO(后进先出) |
用途 | 资源清理、错误处理、日志记录等 |
注意 | 避免在循环中滥用 |