八 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()
}
注意:
recover()
必须搭配defer
使用。defer
一定要在可能引发panic
的语句之前定义。