Go Defer语句详解

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

  • 内存泄漏

  • 文件或连接被长期占用

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

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


defer 语句

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


defer 的基本用法

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

package main

import "fmt"

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

输出结果:

Printed first! 1
Printed second! 2

代码解释

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

这是因为:

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


多个 defer 语句

在 Go 程序中,可以有多个 defer 语句。
当存在多个 defer 时,它们会被当作一个栈来管理

来看下面的例子:

package main

import "fmt"

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

输出结果:

🥚
🐣
🐥

代码解释

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

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

这说明:

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

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

defer 🐥
defer 🐣
defer 🥚  ← 先执行

多个函数中的 defer

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

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

来看下面的例子:

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
}

输出结果:

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

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

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
}

输出结果:

n = 2 - second deferred print
n = 2 - first deferred print

代码解释

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

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

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

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

说明:

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


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

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

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 代码的重要一步。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值