Golang 开发文档 https://www.topgoer.com/%E5%8…
defer 个性:
- 关键字 defer 用于注册提早调用。
- 这些调用直到 return 前才被执。因而,能够用来做资源清理。
- 多个 defer 语句,按先进后出的形式执行。
- defer 语句中的变量,在 defer 申明时就决定了。
defer 用处:
- 敞开文件句柄
- 锁资源开释
- 数据库连贯开释
依据开发文档形容,上面来做一一验证。
1.defer 用于注册提早调用。
package main
import "fmt"
func main() {fmt.Println("测试 defer start")
defer func() {fmt.Println("defer func")
}()
fmt.Println("测试 defer end")
}
执行后果为:
证实 defer 函数用来提早调用,在函数完结的时候被调用。函数完结包含:被调用办法失常 return,或者达到办法体结尾,也或者蕴含 defer 函数的办法在执行 panic 的时候都会执行 defer 函数。
值得注意是,当蕴含 defer 函数的办法在执行 panic 操作的时候,会先执行 defer 函数,再执行 panic 办法,panic 之后的语句是不可达的。上面我用实例来再次证实一下。
package main
import "fmt"
func main() {fmt.Println("测试 defer start")
defer func() {fmt.Println("defer func")
}()
fmt.Println("测试 defer end")
panic("panic 异样")
}
执行后果为:
由此看出,defer 产生在 panic 执行之前,因而,能够在 defer 办法外面做资源清理,开释操作。
2. 多个 defer 语句,按先进后出的形式执行。
package main
import "fmt"
func main() {deferFunc1()
}
func deferFunc1() {
val := "1111"
defer fmt.Println("val1",val)
defer func() {fmt.Println("val2",val)
}()
defer fmt.Println("val3",val)
defer func(val string) {fmt.Println("val4",val)
}(val)
defer fmt.Println("val5",val)
fmt.Println("val",val)
}
执行后果为:
简略了解就是:定义 defer 相似于入栈操作,执行 defer 相似于出栈操作,先进后出。
3.defer 语句中的变量,在 defer 申明时就决定了。
package main
import "fmt"
func main() {deferFunc1()
}
func deferFunc1() {
val := "1111"
defer fmt.Println("val1",val)
defer func() {fmt.Println("val2",val)
}()
defer fmt.Println("val3",val)
val = "2222"
defer func(val string) {fmt.Println("val4",val)
}(val)
defer fmt.Println("val5",val)
val = "3333"
defer func() {fmt.Println("val6",val)
}()
fmt.Println("val",val)
}
此时,能够考虑一下下面程序输入后果是啥?为什么?针对下面程序有几个易错点,常常被作为面试的口试题来考查求职者对 defer 的了解。
上面看一张很经典的图片,golang 程序调用程序图。
var 这些变量是依照由上到下赋值的,联合下面程序来说,val 变量最初被赋值为 ”3333″,而 defer 又是推延函数,按理说所有 defer 蕴含的提早函数的 val 都是 ”3333″ 才对,然而执行后果出乎咱们的预料。
由下面后果能够看出:
defer 间接执行 fmt.Println 函数,val 变量的值对应的由上到下最近赋值变量的值。
defer 执行 func(){}() 时,val 变量的值对应最初赋值的值。这里还有一种非凡状况,defer func(val string) {}(val) 将变量传递到 defer 函数外面,此时 func(val string) val 为形参,它的值受传递的实参来决定的。