defer语句 #
一、defer概述 #
defer语句将函数调用推迟到外层函数返回之前执行。
二、基本用法 #
2.1 基本语法 #
go
defer 函数调用
2.2 示例 #
go
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
// 输出:
// hello
// world
2.3 资源释放 #
go
func readFile(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close() // 确保文件关闭
// 处理文件...
return nil
}
三、执行顺序 #
3.1 后进先出(LIFO) #
多个defer按后进先出顺序执行:
go
func main() {
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
}
// 输出:
// 3
// 2
// 1
3.2 defer栈 #
go
func stack() {
for i := 0; i < 3; i++ {
defer fmt.Println(i)
}
}
// 输出:
// 2
// 1
// 0
四、defer与参数 #
4.1 参数立即求值 #
defer语句中的参数在声明时求值:
go
func main() {
i := 0
defer fmt.Println(i) // 此时i=0
i++
fmt.Println(i) // 1
}
// 输出:
// 1
// 0
4.2 闭包延迟求值 #
使用闭包可以延迟求值:
go
func main() {
i := 0
defer func() {
fmt.Println(i) // 延迟到执行时求值
}()
i++
fmt.Println(i)
}
// 输出:
// 1
// 1
4.3 方法接收者 #
go
type Counter struct {
value int
}
func (c *Counter) Print() {
fmt.Println(c.value)
}
func main() {
c := &Counter{value: 0}
defer c.Print() // 参数c立即求值
c.value = 10
}
// 输出: 10
五、defer与返回值 #
5.1 命名返回值 #
defer可以修改命名返回值:
go
func example() (result int) {
defer func() {
result++
}()
return 10
}
fmt.Println(example()) // 11
5.2 执行时机 #
defer在return之后、函数返回之前执行:
go
func example() (result int) {
defer func() {
fmt.Println("defer:", result)
result++
}()
result = 10
fmt.Println("before return")
return result
}
fmt.Println("result:", example())
// 输出:
// before return
// defer: 10
// result: 11
5.3 匿名返回值 #
匿名返回值不能被defer修改:
go
func example() int {
var result int
defer func() {
result++ // 修改的是局部变量
}()
return result // 返回的是result的副本
}
fmt.Println(example()) // 0
六、实际应用 #
6.1 文件操作 #
go
func readFile(path string) ([]byte, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
return io.ReadAll(file)
}
6.2 数据库连接 #
go
func queryUser(db *sql.DB, id int) (*User, error) {
rows, err := db.Query("SELECT * FROM users WHERE id = ?", id)
if err != nil {
return nil, err
}
defer rows.Close()
var user User
if rows.Next() {
err = rows.Scan(&user.ID, &user.Name, &user.Email)
return &user, err
}
return nil, sql.ErrNoRows
}
6.3 锁释放 #
go
var mu sync.Mutex
func safeOperation() {
mu.Lock()
defer mu.Unlock()
// 安全操作...
}
6.4 计时 #
go
func measureTime(name string) func() {
start := time.Now()
return func() {
fmt.Printf("%s took %v\n", name, time.Since(start))
}
}
func main() {
defer measureTime("main")()
time.Sleep(time.Second)
}
6.5 错误处理 #
go
func doSomething() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v", r)
}
}()
// 可能panic的操作
return nil
}
6.6 HTTP响应体关闭 #
go
func fetch(url string) ([]byte, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
七、defer性能 #
7.1 defer开销 #
defer有少量性能开销,但在大多数情况下可以忽略:
go
// 有defer
func withDefer() {
defer func() {}
}
// 无defer
func withoutDefer() {
}
7.2 优化建议 #
在性能关键的热点代码中,可以考虑避免使用defer:
go
// 使用defer
func processFile(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
// 处理...
return nil
}
// 不使用defer(性能优化)
func processFileOptimized(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
// 处理...
f.Close() // 手动关闭
return nil
}
八、常见错误 #
8.1 defer在循环中 #
go
// 错误:资源不会及时释放
func processFiles(paths []string) error {
for _, path := range paths {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // 所有文件在函数结束时才关闭
// 处理文件...
}
return nil
}
// 正确:使用闭包
func processFiles(paths []string) error {
for _, path := range paths {
if err := processFile(path); err != nil {
return err
}
}
return nil
}
func processFile(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
// 处理文件...
return nil
}
8.2 nil defer #
go
func example() {
var f func()
defer f() // panic: nil function
}
8.3 defer参数求值时机 #
go
func example() {
i := 0
defer fmt.Println(i) // 输出0,不是1
i = 1
}
九、最佳实践 #
9.1 资源获取后立即defer #
go
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // 立即defer
9.2 使用defer处理错误 #
go
func doSomething() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic recovered: %v", r)
}
}()
// 可能panic的代码
return nil
}
9.3 避免在循环中使用defer #
go
// 不好
for _, item := range items {
mu.Lock()
defer mu.Unlock() // 所有锁在函数结束时释放
process(item)
}
// 好
for _, item := range items {
func() {
mu.Lock()
defer mu.Unlock()
process(item)
}()
}
十、总结 #
defer要点:
| 特性 | 说明 |
|---|---|
| 执行时机 | 函数返回前执行 |
| 执行顺序 | 后进先出(LIFO) |
| 参数求值 | 声明时立即求值 |
| 命名返回值 | 可以修改命名返回值 |
关键点:
- 延迟执行:defer在函数返回前执行
- LIFO顺序:多个defer按后进先出执行
- 参数求值:参数在声明时求值
- 资源释放:常用于关闭文件、释放锁等
- 命名返回值:defer可以修改命名返回值
准备好学习递归函数了吗?让我们进入下一章!
最后更新:2026-03-26