defer 是 Go 中延迟执行函数的关键字,用于资源清理和控制执行顺序,按后进先出(LIFO)在函数返回前执行;参数在 defer 语句执行时求值,非调用时;需注意变量捕获、nil 指针及 panic 处理等陷阱。
defer 是 Go 中用于延迟执行函数调用的关键字,常用于资源清理(如关闭文件、释放锁、恢复 panic)和控制执行顺序。它的核心特点是:语句在当前函数返回前执行,但按“后进先出”(LIFO)顺序调用。
defer 语句不是在函数结束时才注册,而是在它被**执行到时立即注册**,但实际调用推迟到外层函数即将返回(包括正常 return 或 panic)之前。注意:defer 表达式中的参数会在 defer 语句执行时求值,而非真正调用时。
defer fmt.Println(i) 中的 i 值在 defer 执行那一刻就确定了,后续修改 i 不影响输出Go 推荐使用 defer 确保资源及时释放,避免遗忘导致泄漏。典型场景是 os.Open + Close 配对:
f, err := os.Open("file.txt")
if err != nil {
log.Fatal(err)
}
defer f.Close() // 即使后面发生 panic 或多处 return,也会执行
Open 错误后再 defer Close,否则可能 panic(nil pointer dereference)io.ReadCloser 等接口,同样适用 —— defer 调用其 Close() 方法即可由于 LIFO 特性,多个 defer 可构成清晰的“进入-退出”逻辑链,适合做日志记录、计时、锁释放等:
func process() {
log.Println("start")
defer log.Println("cleanup 2") // 最后执行
defer log.Println("cleanup 1") // 倒数第二执行
log.Println("doing work")
}
start → doing work → cleanup 1 → cleanup 2
defer func() { if r := recover(); r != nil { log.Printf("recovered: %v", r) } }()
defer 看似简单,但容易因误解求值时机或作用域而出错:
defer resp.Body.Close() 没问题,但 defer f.Close() 若 f 是未初始化的 nil,会 panicdefer fmt.Println(i) 通常输出多个相同值(最后一个 i),应改用 defer func(v int) { ... }(i)
刷新缓冲区,应显式调用 flush(),而非依赖 defer