golang的defer

文章目录

把 defer 想象成"临时便签"

当你写 defer 时,Go 会把这个函数调用写在一张"便签"上,等到当前函数结束时,再执行这些便签上的内容。

用生活例子理解:

go 复制代码
func 回家() {
    fmt.Println("1. 进门")
    
    defer fmt.Println("4. 关灯")     // 便签1:记住要关灯
    defer fmt.Println("3. 锁门")     // 便签2:记住要锁门
    
    fmt.Println("2. 吃饭、看电视...")
    
    // 函数结束时,Go会按照便签执行:
    // 先执行便签2(锁门)
    // 再执行便签1(关灯)
}

输出顺序:

复制代码
1. 进门
2. 吃饭、看电视...
3. 锁门
4. 关灯

更直观的对比:

没有 defer 的代码:

go 复制代码
func openFile() {
    file, _ := os.Open("test.txt")
    
    // 做一些操作...
    if 某个条件 {
        file.Close()  // 要记得关闭文件
        return
    }
    
    // 做更多操作...
    if 另一个条件 {
        file.Close()  // 又要记得关闭文件
        return
    }
    
    file.Close()  // 还要记得关闭文件
}

有 defer 的代码:

go 复制代码
func openFile() {
    file, _ := os.Open("test.txt")
    defer file.Close()  // 一次性"预约"关闭操作
    
    // 做一些操作...
    if 某个条件 {
        return  // 文件会自动关闭
    }
    
    // 做更多操作...
    if 另一个条件 {
        return  // 文件会自动关闭
    }
    
    // 函数结束,文件会自动关闭
}

执行时机的详细说明:

go 复制代码
func example() {
    fmt.Println("开始")
    
    defer fmt.Println("我是defer 1")
    defer fmt.Println("我是defer 2")
    
    fmt.Println("中间")
    
    if true {
        fmt.Println("条件执行")
        return  // 函数在这里返回
    }
    
    fmt.Println("这行不会执行")
}

执行顺序:

  1. fmt.Println("开始")
  2. 遇到 defer fmt.Println("我是defer 1") → 记在便签上
  3. 遇到 defer fmt.Println("我是defer 2") → 记在便签上
  4. fmt.Println("中间")
  5. fmt.Println("条件执行")
  6. return函数要结束了,执行便签!
  7. 执行便签2:fmt.Println("我是defer 2")
  8. 执行便签1:fmt.Println("我是defer 1")

输出:

复制代码
开始
中间
条件执行
我是defer 2
我是defer 1

关键理解点:

  1. "包含它的函数" = 写 defer 的那个函数
  2. "返回之前" = 函数结束的那一刻,但还没有真正返回给调用者
  3. 无论怎么退出 = 正常return、panic、到达函数末尾,defer都会执行

用计时器例子:

go 复制代码
func 做作业() {
    fmt.Println("开始做作业")
    
    defer fmt.Println("收拾桌子")  // 便签:记住要收拾桌子
    
    fmt.Println("做数学题")
    fmt.Println("做语文题")
    
    // 函数结束时,自动执行:收拾桌子
}

这样理解了吗?defer 就像是在函数里贴便签,提醒自己在离开前要做什么事情!