Go语言的defer关键字用于延迟调用,下面是关于Go语言defer关键字的一些基础概念:

1. defer关键字用于注册延迟调用;

2. 这些调用直到包含当前该defer关键字的函数执行完了才会被执行;

3. 如果定义了多个defer语句,按照先进后出的方式执行,也即后定义的defer语句会先被执行,这也比较好理解,如果之前的资源释放掉了,后面需要使用掉之前资源的语句就没法执行了(可以用来逆序输出内容);

4. defer语句中的变量,在defer声明的时候就决定了,也即在defer语句中使用到的变量的值为在defer语句之前的变量值(不包括匿名函)

defer语句主要有三个方面的用途:① 关闭文件句柄;② 锁资源释放;③ 数据库连接释放;通过defer机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放,主要用于资源的管理;

多个defer关键字的执行顺序,下面的例子输出的数字为:3 2 1:

package mainimport ("fmt"
)func f1() {defer fmt.Println(1)defer fmt.Println(2)defer fmt.Println(3)
}func main() {f1()
}

延迟调用参数在注册时求值或复制,可用指针或闭包 "延迟" 读取:

package mainimport ("fmt"
)func f1() {x, y := 0, 0x += 1// 闭包环境defer func(i int) {fmt.Print(i, " ", x, " ")}(x)defer func() {fmt.Print(y, " ")}()defer func() {fmt.Print(x+y, " ")}()x += 2y += 2
}func main() {f1()fmt.Println()f1()
}

输出结果:

5 2 1 3
5 2 1 3 

defer遇到闭包:

package mainimport "fmt"func f1() {a := []int{1, 4, 6, 2, 7}for _, v := range a {defer func() {fmt.Println(v)}()}
}func main() {f1()
}

输出结果:由于闭包使用到的变量v在执行的时候已经变成7了,所以所有的输出结果都为7:

7
7
7
7
7
package mainimport "fmt"func f(x int) int {fmt.Printf("传递的参数地址: %p\n", &x)defer fmt.Println("1: ", x)defer func() {x += 1fmt.Printf("defer函数中参数地址: %p\n", &x)}()fmt.Println("2: ", x)return x
}func main() {fmt.Println("3: ", f(10))
}

输出结果:

传递的参数地址: 0xc000018098
2:  10
defer函数中参数地址: 0xc000018098
1:  10
3:  10  

容易发生的错误:当方法接受者为指针类型的函数的时候,如果调用这种类型的函数那么使用循环输出值的时候为最后一个元素的值:

package mainimport "fmt"type Stu struct {name string
}func (stu *Stu) Test() {fmt.Println(stu.name)
}func main() {stus := []Stu{{"a"}, {"b"}, {"c"}}for _, v := range stus {defer v.Test()}
}

输出结果:

c
c
c

我们可以修改一下上面例子增加一个接受者为值类型的Test2函数,在Test2函数中调用Test函数,那么就可以得到我们预期的结果:

package mainimport "fmt"type Stu struct {name string
}func (stu *Stu) Test() {fmt.Println(stu.name)
}func (stu Stu) Test2() {stu.Test()
}func main() {stus := []Stu{{"a"}, {"b"}, {"c"}}for _, v := range stus {defer v.Test2()}
}

或者使用下面的方式,也可以输出预期的结果:

package mainimport "fmt"type Stu struct {name string
}func (stu *Stu) Test() {fmt.Println(stu.name)
}func main() {stus := []Stu{{"a"}, {"b"}, {"c"}}for _, v := range stus {v1 := vdefer v1.Test()}
}

从上面的例子中可以看到defer语句在执行的时候,函数调用的参数会被保存起来,但是不执行,复制了一份,并且从这个例子中可以发现go语言并没有把结构体指针类型的变量看成是参数。

当存在多个defer语句注册的时候,按照先进后出的方式执行,如果某个函数或者延迟调用发生错误,这些调用依旧会被执行:

package mainimport "fmt"func f(a, b int) {defer func() {fmt.Println(a / b)}()defer fmt.Println(a)defer fmt.Println(b)
}func main() {f(1, 0)
}

defer与return语句的使用:在具有具体名字返回值的函数中(也即返回值有具体的变量名字,下面这个例子为变量i),defer语句可以修改函数中命名返回值:

package mainimport "fmt"func f(a int) (i int) {defer func() {i += 20}()return a + 10
}
func main() {fmt.Println(f(0))
}

输出结果为:30

defer相关的输出结果例子:

package mainimport "fmt"func f1() {for i := 0; i < 5; i++ {defer fmt.Println(i)}
}func f2() {for i := 0; i < 5; i++ {defer func() {fmt.Println(i)}()}
}func f3() {for i := 0; i < 5; i++ {defer func(n int) {fmt.Println(n)}(i)}
}func f4() int {t := 5defer func() {t++}()return t
}func f5() (r int) {defer func() {r++}()return 0
}func f6() (r int) {t := 5defer func() {t = t + 5}()return t
}func f7() (r int) {fmt.Printf("%p\n", &r)defer func(r int) {fmt.Printf("%p\n", &r)r = r + 5}(r)return 1
}func main() {f7()
}

f1输出结果:

4
3
2
1
0

f2输出结果:

5
5
5
5
5

f3输出结果:

4
3
2
1
0

调用f4输出结果:5

调用f5输出结果:1

调用f6输出结果:5

调用f7输出结果:

0xc000018098
0xc0000180d0
1 

Go语言defer关键字相关推荐

  1. Go 语言编程 — defer 关键字

    目录 文章目录 目录 defer 关键字 defer 的用途之一:释放资源 defer 的用途之二:执行 recover(恢复) defer 特性 多个 defer 的执行顺序 被 deferred ...

  2. java怎么延迟执行语句_Go语言defer(延迟执行语句)

    Go语言中关键字defer允许我们推迟到函数返回之前(或任意位置执行return语句之后)一刻才执行某个语句或函数(为什么要在返回之后才执行这些语句?因为return语句同样可以包含一些操作,而不是单 ...

  3. Go语言defer详解笔记

    Go语言defer详解 1.defer概述: ​ defer是用来声明一个延迟函数,并且将这个函数放到一个栈中,它的调用时间在return执行之前,详细来讲,它的执行时间在return的值赋值之后,在 ...

  4. Java语言的关键字

    Abstract 抽象的 一个Java语言中的关键字,用在类的声明中来指明一个类是不能被实例化的,但是可以被其它类继承.一个抽象类可以使用抽象方法,抽象方法不需要实现,但是需要在子类中被实现 brea ...

  5. 返回语句C语言return关键字

    时间紧张,先记一笔,后续优化与完善. C语言return关键字 return 用来终止一个函数并返回其前面跟着的值,绝对很简单. return (Val)://此括号可以省略.但一般不省略,尤其在返回 ...

  6. C语言的关键字 extern

    该博文为原创文章,未经博主同意不得转载,如同意转载请注明博文出处 本文章博客地址:https://cplusplus.blog.csdn.net/article/details/105112033 C ...

  7. c语言中switch语句流程图_C语言:C语言保留字(关键字)

    保留字(reserved word) 保留字又称关键字. 指在高级语言中已经定义过的字,使用者不能再将这些字作为变量名或过程名使用. 每种程序设计语言都规定了自己的一套保留字. 例如:BASIC语言规 ...

  8. C语言register关键字——最快的关键字

    C语言register关键字-最快的关键字 (2012-08-24 14:09:24) 转载▼ 标签: 杂谈 分类: C/C    register:这个关键字请求编译器尽可能的将变量存在CPU内部寄 ...

  9. sizeof不是java关键字是_下列哪项不是Java语言的关键字。

    下列哪项不是Java语言的关键字. A:instanceof B:goto C:volatile D:sizeof 正确答案:sizeof 解析: 下列哪项不是Java语言的关键字. A:instan ...

最新文章

  1. 性能指标:QPS、TPS、RT、吞吐量
  2. 获取Android studio 中的模拟器的界面的点的坐标(Ubuntu)
  3. Hadoop单机环境配置
  4. sql server关闭存储过程中未提交的事务
  5. 什么是转向灯?使用转向灯有何技巧?
  6. 【转】如何让ucgui支持24位色(24bpp)
  7. Python与机器视觉(x)windows下import cv2报错dll
  8. 2021瑞安高考成绩查询,温州教育网官网登录入口2021瑞安中考成绩查询中招查分系统...
  9. Java 算法 理财计划
  10. 蓝天集团董事长郎凤娥专访
  11. JavaScript事件 以及和jQuery中事件使用对比
  12. java语言实现任何一种排序_请用java语言编写排序程序。
  13. Flutter入门学习--(18)添加本地资源图片
  14. 电子设计大赛应该准备什么
  15. Office | Office365 离线安装包选择安装word、ppt、excel
  16. 功能测试项目——酒店管理系统
  17. GaussDB常用命令
  18. 海龟如何保留米帝手机号
  19. js格式化时间(YYYY-MM-DD)
  20. oracle闪回技术详解之闪回drop(神奇的flashback)

热门文章

  1. Error response from daemon: conflict: unable to delete 6f8214d56bfc (cannot be forced) - image has d
  2. 原生小程序下载功能(直接复制用就行了)
  3. arduino控制颜色传感器
  4. Jamf Pro 10.27: IT管理员的幸福指数的又一次提升
  5. 骨架图的模拟js实现
  6. 【JavaWeb学习笔记】之HTMLCSS
  7. 如何完成学术论文的修改和润色工作? - 易智编译EaseEditing
  8. AlphaGo简单解析
  9. fortify扫描java_fortify代码扫描使用教程(20.0版本)
  10. SpringBoot2访问webapp文件404