在编程中,我们经常有读取文件或者连接数据库资源等操作,每次读取文件后,我们就要关闭文件流,每次连接数据库后,我们都需要断开连接。
在Java中,我们利用 try-catch-finally来处理这些问题,把要关闭的资源放在finally中,确保每次关闭资源都会被执行到,而且这样写,就不会忘记关闭资源了,也让我们知道在何时何地可以关闭资源,因为这个 try-catch-finally是可以成对出现的。而在Go中,没有 try-catch-finally来确保我们每次不会忘记关闭资源,也因为没有这个关键字让我们不知道在什么时候关闭资源,但是呢,Go语言提供了一个叫做 defer的关键字,来确保我们可以每次使用资源后,都可以不会忘记关闭资源,也解决了我们不知道在什么时候关闭资源的问题,那么 defer到底有什么用呢?
defer的介绍
defer关键字,从它的字面意思看,可以看出它有推迟的意思,所以它的功能是推迟执行defer关键字后面所带的内容,然后在函数的最后面执行这些内容推迟的内容
如果有多个defer关键字,那么这些defer关键字最后的执行顺序是什么?
defer遵循着先进后出的规则,也就是你越先写的defer,在函数最后面越晚执行到
只有一个defer的情况
使用defer关键字前的执行顺序
package main
import "fmt"
func main() {
fmt.Println("连接数据库")
fmt.Println("关闭数据库")
fmt.Println("-----------")
}
//输出:
//连接数据库
//关闭数据库
//-----------
从输出我们可以看出,我们在连接数据库后,后面就调用了关闭数据库的操作(虽然这里是打印而已,不过不要太在意),然后在函数最后还写了个打印语句。这样看起来是没问题,但是我们如果还要进行其它关于数据库的操作,那么连接数据库后,就关闭显然不太合适了,而且我们也不知道连接之后,在哪里还需要操作,所以对于这个关闭操作,放在哪里就有点让人纠结了。
不过没什么关系,我们只要确保每次连接数据库后,在最后函数结束的时候,能帮我们关闭连接资源就好,所以这时候用defer关键字最好
使用defer关键字的情况
package main
import "fmt"
func main() {
fmt.Println("连接数据库")
defer fmt.Println("关闭数据库")
fmt.Println("-----------")
}
//输出:
//连接数据库
//-----------
//关闭数据库
从这里的输出我们看出来了,添加了defer关键字后,这个关闭资源的操作就在函数最后面执行了,不过这个还说的不太好,我们多加几个语句看看
package main
import "fmt"
func main() {
fmt.Println("连接数据库")
defer fmt.Println("关闭数据库")
fmt.Println("-----------")
fmt.Println("AAAAAAAAAA")
fmt.Println("BBBBBBBBBB")
}
输出
连接数据库
-----------
AAAAAAAAAA
BBBBBBBBBB
关闭数据库
这个例子比上面那个更能说明情况了
多个defer的执行顺序
package main
import "fmt"
func main() {
fmt.Println("多个defer的执行顺序")
defer fmt.Println("one")
fmt.Println("---------------")
defer fmt.Println("two")
fmt.Println("---------------")
defer fmt.Println("three")
fmt.Println("---------------")
}
输出
多个defer的执行顺序
---------------
---------------
---------------
three
two
one
从最后的输出顺序,我们可以看出,越后调用的defer,越先执行
defer后面可以跟哪些内容
可以跟随变量?
package main
import "fmt"
func main() {
fmt.Println("defer后面可以跟随的内容")
var count int = 555
defer count
}
答案是不可以的,我们在defer后面跟随变量,会报错,报出的错误如下
Expression in defer must be function call
我用百度翻译了一下,它说的是「defer中的表达式必须是函数调用」,所以我们后面要跟的是函数调用才行,因为输出语句也是函数调用啊...
跟随函数的例子
package main
import "fmt"
func main() {
fmt.Println("defer后面可以跟随的内容")
defer func() {
fmt.Println("我是匿名函数,我也可以执行")
}()
}
输出
defer后面可以跟随的内容
我是匿名函数,我也可以执行
在这里,我就用匿名函数替代函数调用好了
在什么情况下,defer语句不会执行
在return后面的defer不会被执行
package main
import "fmt"
func main() {
defer fmt.Println("我在return语句前面")
return
defer fmt.Println("我在return语句后面")
}
我就知道这一种情况,等以后知道多了一些,记得的话就回来补充
欢迎大家关注下个人的「公众号」:独醉贪欢