defer
是Golang一大语言特色,它扮演的是类似Java中finally
的角色。用于执行释放资源的一些操作。
defer语句定义在函数内,后面跟一个函数,被defer的函数会在defer所在的函数结果前被执行。defer能够保证函数不管以何种方式结束(return或panic),被defer的函数一定被执行。
释放资源
defer可以被定义在函数中的任何地方,这意味着当我们打开一个资源,马上可以使用defer声明它的释放,这样我们在编写后面的代码的时候就不用操心这个资源的释放了。函数结束的时候会自动执行释放操作:
func ReadFile() error {
// 打开了一些资源
file, err := os.Open("file")
if err != nil {
return err
}
// 这条语句会自动在函数结束时执行
defer file.Close()
// 继续执行后面的操作,而不用操心file的释放了
...
}
这就是defer的一般用法了,当然,defer远没有这么简单,下面我们来扣扣defer的语法细节:
多个defer的执行顺序
如果一个函数里面有多个defer,那么会如何执行呢?观察下面的代码:
package main
import "fmt"
func main() {
deferFunc()
}
func deferFunc() {
defer fmt.Println("exec 1")
defer fmt.Println("exec 2")
defer fmt.Println("exec 3")
}
注意,defer的执行顺序遵循后进先出
的原则,后面的defer语句将会被先执行,因此执行的顺序应该是从下至上的,下面是输出:
exec 3
exec 2
exec 1
我们只需要知道defer是从下到上执行的即可。
defer和return的顺序
defer的顺序还有更加深层次的话题,那就是defer和return之间的执行顺序。
观察下面的代码:
package main
import "fmt"
func main() {
fmt.Println(deferReturn())
}
func deferReturn() (i int) {
defer func() {
i += 3
}()
i = 3
return
}
执行一下,结果输出"6"
。这说明,defer是在函数return之前执行的。而具名返回参数i
的作用域是整个函数,因此理所当然地,defer可以访问i
并对其做修改。
defer和panic
当函数遇到panic
,defer仍然会被执行。Go会先执行所有的defer链表(该函数的所有defer),当所有defer被执行完毕且没有recover
时,才会进行panic。
package main
import