【go】 Go语言中的 defer:从入门到理解底层机制(讲透版)

👨‍💻 关于作者:会编程的土豆

"不是因为看见希望才坚持,而是坚持了才看见希望。"

你好,我是会编程的土豆,一名热爱后端技术的Java学习者。

📚 正在更新中的专栏:

💕作者简介:后端学习者

defer 是 Go 里一个非常"有味道"的关键字。

很多人一开始只会用,但其实不理解执行顺序和坑点,很容易写错代码

这篇直接帮你彻底搞懂。


一、defer 是什么?

一句话:

复制代码
defer = 延迟执行,在函数结束前执行

最简单例子

Go 复制代码
func main() {
    fmt.Println("start")

    defer fmt.Println("defer")

    fmt.Println("end")
}

输出

复制代码
start
end
defer

核心结论

复制代码
defer 一定在函数 return 之前执行

二、多个 defer 的执行顺序(重点)


示例

Go 复制代码
func main() {
    defer fmt.Println(1)
    defer fmt.Println(2)
    defer fmt.Println(3)
}

输出

复制代码
3
2
1

结论

复制代码
defer 是栈结构(后进先出 LIFO)

👉 类比:

复制代码
像压栈 → 出栈

三、defer + return(高频考点)


示例

Go 复制代码
func test() int {
    defer fmt.Println("defer")
    return 10
}

执行顺序(非常重要)

复制代码
1. 计算 return 的值
2. 执行 defer
3. 返回

四、经典陷阱:defer 修改返回值


示例1(无效修改)

Go 复制代码
func test() int {
    a := 10
    defer func() {
        a = 20
    }()
    return a
}

返回值

复制代码
10

为什么?

复制代码
return a 已经把值拷贝走了

示例2(有效修改)

Go 复制代码
func test() (a int) {
    a = 10
    defer func() {
        a = 20
    }()
    return
}

返回值

复制代码
20

关键区别

复制代码
✔ 命名返回值 → defer 可以修改
✔ 普通返回值 → defer 修改不了

五、defer + 参数(超级容易错)


示例

Go 复制代码
func main() {
    a := 10

    defer fmt.Println(a)

    a = 20
}

输出

复制代码
10

原因

复制代码
defer 在注册时就把参数"拷贝"了

对比:闭包写法

Go 复制代码
defer func() {
    fmt.Println(a)
}()

输出

复制代码
20

结论

复制代码
defer参数:立即求值
defer函数:延迟执行

六、defer 的典型应用场景


1. 关闭资源(最重要)

Go 复制代码
file, _ := os.Open("test.txt")
defer file.Close()

2. 解锁

Go 复制代码
mu.Lock()
defer mu.Unlock()

3. 计时

Go 复制代码
start := time.Now()
defer func() {
    fmt.Println(time.Since(start))
}()

七、defer 的底层理解(面试加分)


本质

复制代码
defer = 把函数压入一个栈

执行时机

复制代码
函数 return 前,按栈顺序执行

八、性能问题(进阶)


早期 Go

复制代码
defer 较慢

现在(Go 1.20+)

复制代码
优化后基本可以放心使用

九、常见坑总结(必须记住)


1️⃣ defer 顺序是反的

复制代码
后写先执行

2️⃣ 参数是立即计算

复制代码
defer fmt.Println(a)  // 立即拷贝

3️⃣ 命名返回值可以被修改

复制代码
func f() (a int)

4️⃣ defer 不要写在循环里(可能性能问题)


十、一句话总结

复制代码
defer = 注册一个"收尾操作",在函数退出前按栈顺序执行
相关推荐
2601_962071573 小时前
【Java报错已解决】org.springframework.beans.factory.BeanCreationException
java·开发语言
淡海水4 小时前
07-04-并发-ConcurrentBag-T-工作窃取WorkStealing算法
开发语言·算法·c#·bag·concurrent·workstealing
CS_Zero5 小时前
C语言sizeof是函数吗?
c语言·开发语言
东风破_5 小时前
从跨域到 WebSocket:前端跨域方案、SSE 与双向实时通信详解
前端·后端
-今昭-6 小时前
《V2V 迁移实战:将 VMware 虚拟机转为 OpenStack 可用 Glance qcow2 镜像》
开发语言·python
行百里er7 小时前
Gateway 上别硬塞 MVC:DeferredImportSelector 看菜下锅
spring boot·后端·开源
名字还没想好☜7 小时前
Java 遍历时删元素抛 ConcurrentModificationException:fail-fast 原理与三种正确删法
后端
IT_陈寒8 小时前
React Hooks闭包陷阱差点让我加班到凌晨
前端·人工智能·后端
黑马程序员毕设8 小时前
基于Java的医院药品管理系统的优化设计与实现
java·开发语言·spring boot·小程序·架构·课程设计·毕设
码事漫谈8 小时前
本体论,彻底懂
后端