26 函数之内置函数

八 defer语句

8.1 defer介绍

Go语言中,函数内可以依次包含多个defer语句,在函数结束时,defer语句会逆序执行

func main() {
    fmt.Println("start...")
    defer fmt.Println(1)
    defer fmt.Println(2)
    defer fmt.Println(3)
    fmt.Println("end...")
}

输出

start...
end...
3
2
1

由于defer语句延迟调用的特性,所以defer语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、解锁及记录时间等。

8.2 defer的执行时机

在Go语言的函数中return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer语句执行的时机就在返回值赋值操作后,RET指令执行前。具体如下图所示:

8.3 defer经典案例

下述代码运行结果是???

package main

import "fmt"

// 关键看,defer定义的函数内部是否引用的是func的返回值,如果是,则修改的就是返回值
func f1() int {
    x := 5
    defer func() {
        x++ // 此处的x引用的是哪个位置的?x++是否影响返回值?
        fmt.Println("f1===>",x) // 6
    }()
    return x
}
func f2() (y int) {
    x := 5
    defer func() {
        x++ // 此处的x引用的是哪个位置的?x++是否影响返回值?
        //y++
        fmt.Println("f2===>",x)  // 6
    }()
    return x
}
func f3() (x int) {
    defer func(x int) {
        x++ // 此处的x引用的是哪个位置的?x++是否影响返回值?
        fmt.Println("f3===>",x)  // 1

    }(x)
    return 5
}
func f4() (x int) {
    defer func() {
        x++ // 此处的x引用的是哪个位置的?x++是否影响返回值?
        fmt.Println("f4===>",x)  // 6

    }()
    return 5
}

func main() {
    fmt.Println(f1())
    fmt.Println(f2())
    fmt.Println(f3())
    fmt.Println(f4())
}

8.4 defer面试题

下述代码输出结果为???提示:在defer注册要延迟执行的函数时,该函数所有的参数的值都会被确定下来

func calc(index string, a, b int) int {
    ret := a + b
    fmt.Println(index, a, b, ret)
    return ret
}

func main() {
    x := 1
    y := 2
    defer calc("AA", x, calc("A", x, y))
    x = 10
    defer calc("BB", x, calc("B", x, y))
    y = 20
}

九 内置函数

panic/recover

Go语言使用panic/recover模式来处理错误。 panic可以在任何地方引发,但recover只有在defer调用的函数中有效。 首先来看一个例子:

func funcA() {
    fmt.Println("func A")
}

func funcB() {
    panic("panic in B")  // 主动抛出panic
}

func funcC() {
    fmt.Println("func C")
}
func main() {
    funcA()
    funcB()
    funcC()
}

输出:

func A
panic: panic in B

goroutine 1 [running]:
main.funcB(...)
        .../code/func/main.go:12
main.main()
        .../code/func/main.go:20 +0x98

程序运行期间funcB中引发了panic导致程序崩溃,异常退出了。这个时候我们就可以通过recover将程序恢复回来,继续往后执行。

func funcA() {
    fmt.Println("func A")
}

func funcB() {
    defer func() {
        err := recover()
        //如果程序出出现了panic错误,可以通过recover恢复过来
        if err != nil {
            fmt.Println("recover in B")
        }
    }()
    panic("panic in B")
}

func funcC() {
    fmt.Println("func C")
}
func main() {
    funcA()
    funcB()
    funcC()
}

注意:

  1. recover()必须搭配defer使用。
  2. defer一定要在可能引发panic的语句之前定义。
上一篇
下一篇
Copyright © 2022 Egon的技术星球 egonlin.com 版权所有 帮助IT小伙伴学到真正的技术