defer
用来声明一个延迟函数,把这个函数放入到一个栈上, 当外部的包含方法return以前,返回参数到调用方法以前调用,也能够说是运行到最外层方法体的"}"时调用。咱们常常用他来作一些资源的释放,好比关闭io操做bash
func doSomething(fileName string) {
file,err := os.Open(fileName)
if err != nil {
panic(err)
}
defer file.Close()
}
复制代码
defer
能够保证方法能够在外围函数返回以前调用。有点像其余言的 try finally
闭包
try{
}finally{
}
复制代码
defer
声明的函数读写外部变量,和闭包差很少。好比下面的代码函数
func doSomething() {
v := 10
defer func() {
fmt.Println(v)
v++
fmt.Println(v)
}()
v += 5
}
复制代码
输出为ui
15
16
复制代码
就像闭包
同样,若是不是defer
函数方法内的变量会向上一层函数访问变量,从新作计算。spa
这个例子中,defer
声明的方法,给命名的返回值自增1code
1 func doSomething() (rev int) {
2 defer func() {
3 rev++
4 }()
5
6 return 5
7 }
复制代码
第6行
的return
至关于资源
return rev = 5
复制代码
defer
声明的匿名函数会在return
以前执行,至关于string
rev = 5
// 执行defer方法
rev++
//而后return
return
复制代码
因此结果是6
我把代码作一点点修改it
1 func doSomething() (rev int) {
2 v := 10
3 defer func() {
4 v++
5 }()
6
7 return v
8 }
复制代码
第7行返回的是局部变量v. io
return v 至关于 return rev = v
复制代码
defer
函数里是对局部变量v
的操做,因此与返回的rev
没有关系。
全部执行的结果是:10
当有多个defer时执行顺序逆向的,后进先出:
func doSomething() {
defer fmt.Println(1)
defer fmt.Println(2)
}
复制代码
会先输出2,再输出1
panic抛出异常后,若是不处理应用程序会崩溃。为了防止程序崩溃,咱们能够在defer
的函数里使用recover
来捕获中异常:
func doSomething() {
defer func() {
if err := recover(); err != nil {
fmt.Print(err)
}
}()
fmt.Println("Running...")
panic("run error")
}
复制代码
输出:
Running...
run error
复制代码
recover
会捕获panic
的异常。我再把代码作一点点修改:
func doSomething() {
defer func() {
if err := recover(); err != nil {
fmt.Print(err)
}
}()
defer func() {
panic("defer error")
}()
fmt.Println("Running...")
panic("run error")
}
复制代码
输出结果
Running...
defer error
复制代码
由于 recover()
只捕获最后一次panic