defer关键字

121 阅读4分钟

在编程中,我们经常有读取文件或者连接数据库资源等操作,每次读取文件后,我们就要关闭文件流,每次连接数据库后,我们都需要断开连接

在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语句后面")
}

我就知道这一种情况,等以后知道多了一些,记得的话就回来补充


欢迎大家关注下个人的「公众号」:独醉贪欢