Golang开发文档 https://www.topgoer.com/%E5%8...
defer个性:
- 关键字 defer 用于注册提早调用。
- 这些调用直到 return 前才被执。因而,能够用来做资源清理。
- 多个defer语句,按先进后出的形式执行。
- defer语句中的变量,在defer申明时就决定了。
defer用处:
- 敞开文件句柄
- 锁资源开释
- 数据库连贯开释
依据开发文档形容,上面来做一一验证。
1.defer用于注册提早调用。
package mainimport "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 mainimport "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 mainimport "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 mainimport "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为形参,它的值受传递的实参来决定的。