在实际编程中,我们经常需要清理一些资源,比如打开的文件、数据库连接等。当程序不再使用这些资源时,及时关闭它们非常重要,否则可能会造成:
-
内存泄漏
-
文件或连接被长期占用
-
其他程序无法访问这些资源
在本节中,我们将学习 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.
代码解释
执行顺序如下:
-
greeting()内部先打印
→Hello, JB Academy! -
greeting()结束
→ 执行其defer -
回到
main()
→ 打印普通语句 -
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 代码的重要一步。
1030

被折叠的 条评论
为什么被折叠?



