Go Defer语句详解

在实际编程中,我们经常需要清理一些资源,比如打开的文件数据库连接 等。当程序不再使用这些资源时,及时关闭它们非常重要,否则可能会造成:

  • 内存泄漏

  • 文件或连接被长期占用

  • 其他程序无法访问这些资源

在本节中,我们将学习 Go 语言中的一个特殊语句,它可以帮助我们在程序执行过程中自动清理资源 ,让代码更加简洁、安全、不易出错 ,并且让"关闭资源"的代码紧挨着"打开资源"的代码,提高可读性。


defer 语句

在 Go 中,我们使用 defer 语句延迟执行一个函数 ,直到包含它的函数即将返回时才执行。


defer 的基本用法

下面是一个最基础的例子:

go 复制代码
package main

import "fmt"

func main() {
    defer fmt.Println("Printed second! 2")
    fmt.Println("Printed first! 1")
}

输出结果:

go 复制代码
Printed first! 1
Printed second! 2

代码解释

虽然 "Printed second! 2" 在代码中先出现,但它并没有先输出。

这是因为:

任何使用 defer 修饰的语句,都会等到当前函数执行结束时才被调用。


多个 defer 语句

在 Go 程序中,可以有多个 defer 语句。

当存在多个 defer 时,它们会被当作一个栈来管理

来看下面的例子:

go 复制代码
package main

import "fmt"

func main() {
    defer fmt.Println("🐥") // 第 1 个 defer
    defer fmt.Println("🐣") // 第 2 个 defer
    defer fmt.Println("🥚") // 第 3 个 defer
}

输出结果:

go 复制代码
🥚
🐣
🐥

代码解释

  • 🐥最先被 defer 的 ,但最后执行

  • 🥚最后被 defer 的 ,却最先执行

这说明:

defer 的执行顺序是 后进先出(LIFO,Last In First Out)

可以把它理解为一个栈结构

go 复制代码
defer 🐥
defer 🐣
defer 🥚  ← 先执行

多个函数中的 defer

当多个函数中都包含 defer 时,需要注意:

defer 只在它所在的函数结束时才会执行

来看下面的例子:

go 复制代码
package main

import "fmt"

func greeting() {
    defer fmt.Println("Printed after Hello, JB Academy!") // 2
    fmt.Println("Hello, JB Academy!") // 1
}

func main() {
    defer fmt.Println("Printed after the main() function is completed.") // 4

    greeting()

    fmt.Println("Printed after calling the greeting() function.") // 3
}

输出结果:

go 复制代码
Hello, JB Academy!
Printed after Hello, JB Academy!
Printed after calling the greeting() function.
Printed after the main() function is completed.

代码解释

执行顺序如下:

  1. greeting() 内部先打印

    Hello, JB Academy!

  2. greeting() 结束

    → 执行其 defer

  3. 回到 main()

    → 打印普通语句

  4. main() 结束

    → 执行 main() 中的 defer

结论:

每个函数中的 defer,都会在该函数结束时立即执行,与其他函数互不影响。


作用域中的 defer

再看一个关于作用域的例子:

go 复制代码
func scopedDefer() {
    n := 0
    defer func() { fmt.Println("n =", n, "- first deferred print") }()
    {
        defer func() { fmt.Println("n =", n, "- second deferred print") }()
        n++ // n = 1
    }
    n++ // n = 2
}

输出结果:

go 复制代码
n = 2 - second deferred print
n = 2 - first deferred print

代码解释

  • 两个 defer 都在 scopedDefer() 函数中

  • 即使其中一个写在代码块 {} 内,它们也不会提前执行

  • 都要等到 scopedDefer() 函数结束

  • 执行时,变量 n 的值已经变成 2

说明:

defer 的执行时间与 函数结束有关,而不是代码块结束。


使用 defer 关闭文件(最常见用法)

defer 最常见、最重要的用途之一,就是释放资源,例如关闭文件。

go 复制代码
package main

import (
    "fmt"
    "log"
    "os"
)

func main() {
    file, err := os.Create("test.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close() // 程序结束前自动关闭文件

    if _, err := fmt.Fprintln(file, "Hello World!"); err != nil {
        log.Fatal(err)
    }
}

代码解释

  • os.Create() 创建并打开文件

  • defer file.Close() 保证函数结束前文件一定会被关闭

  • 即使中途 return 或发生错误,也不会忘记关闭文件


为什么要用 defer 关闭资源?

使用 defer 有两个明显优点:

1. 防止忘记关闭资源

如果以后给函数增加新的 return 路径,也不用担心遗漏 Close()

2. 代码更清晰

"打开资源"和"关闭资源"写在一起,可读性更好,而不是把 Close() 放在函数结尾。


总结

在本节中,主要内容包括:

  • defer 会在当前函数返回前执行

  • 多个 defer后进先出(LIFO) 顺序执行

  • defer 的作用域是 函数级别

  • defer 最常见的用途是 关闭文件、释放资源

掌握 defer 是写出安全、优雅 Go 代码的重要一步。

相关推荐
学不思则罔12 分钟前
ParallelStream并发陷阱解析
java·开发语言·windows
认真的小羽❅16 分钟前
【Java并发编程】volatile关键字深度解析:从内存语义到实际应用
java·开发语言
hahaha 1hhh27 分钟前
用SSH 建立了一个本地端口转发隧道,用于安全地访问远程服务器上的服务,后台运行。autodl
服务器·安全·ssh
奋斗的小乌龟30 分钟前
langchain4j笔记-08
java·spring boot·笔记
leonidZhao33 分钟前
Java25新特性:加密对象的PEM编码
java
计算机安禾36 分钟前
【c++面向对象编程】第21篇:运算符重载基础:语法、规则与不可重载的运算符
java·前端·c++
萧曵 丶37 分钟前
JUC 实际业务高频面试题浅谈
java·juc·aqs·lock
初圣魔门首席弟子39 分钟前
bug 2026.05.15(以前能运行的java springboot项目突然间不能运行后台数据了)
java·开发语言·bug
古怪今人1 小时前
项目和模块 一个目录下创建多个项目 IDEA Multi-Project Workspace插件
java·ide·intellij-idea
小英雄大肚腩丶1 小时前
RabbitMQ消息队列
java·数据结构·spring boot·分布式·rabbitmq·java-rabbitmq