Golang 入门系列(十四)defer, panic和recover用法
以前讲过golang 的基本语法。但是,只是讲了一些基础的语法,感兴趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.html,前段时间有人问我defer,recover的用法。所以,还是统一的总结一下相关的关键字吧。
一. defer 用法
defer的特性: 在函数返回之前, 调用defer函数的操作, 简化函数的清理工作.
在初接触到go时, 就被defer吸引住了,但是在使用defer关键字的时候,还是得注意这些:
1. 在defer表达式确定的时候,defer修饰的函数(后面统称为defered函数)的参数也就确定了
package main
import (
"fmt"
)
func main() {
g()
}
func g() {
i := 0
defer fmt.Println(i)
i++
return
}
-------output-------
0
2. 函数内可以有多个defered函数,但是这些defered函数在函数返回时遵守后进先出的原则
package main
import "fmt"
func main() {
g()
}
func g() {
for i := 0; i<4; i++ {
defer fmt.Println(i)
}
}
-------output-------
3
2
1
0
3. 函数命名的返回值跟defered函数一起使用
函数的返回值有可能被defer更改,本质原因是return xxx语句并不是一条原子指令,执行过程是: 保存返回值(若有)-->执行defer(若有)-->执行return跳转。
func f() (result int) { defer func() { result++ }() return 0 } func g() (r int) { t := 5 defer func() { t = t + 5 }() return t } func h() (r int) { defer func(r int) { r = r + 5 }(r) return 1 }
-------output-------
0
对于defered函数跟函数命名返回值一块使用的情况, 当无法判断返回值的时候, 需要对函数进行变形.
func f(result int) { result = 0 func () { result++ }() return }
-------output-------
1
func g() (r int) { t := 5 r = t func () { t = t + 5 } return }
-------output-------
5
func h() (r int) { r = 1 func (r int) { r = r + 5 }(r) return }
-------output-------
1
在func(r int) {...}中,由于r是以值传递的方式进行的, 所以r的值不会改变。
注意:
1. 申请资源后最好立即使用defer关闭资源。
二. panic用法
panic用法挺简单的, 其实就是throw exception。
panic是golang的内建函数,panic会中断函数F的正常执行流程, 从F函数中跳出来, 跳回到F函数的调用者. 对于调用者来说, F看起来就是一个panic, 所以调用者会继续向上跳出, 直到当前goroutine返回. 在跳出的过程中, 进程会保持这个函数栈. 当goroutine退出时, 程序会crash。
要注意的是, F函数中的defered函数会正常执行, 按照上面defer的规则。
同时引起panic除了我们主动调用panic之外, 其他的任何运行时错误, 例如数组越界都会造成panic
看下面一个例子
package main import ( "fmt" ) func main() { test() } func test() { defer func() { fmt.Println("打印前") }() defer func() { fmt.Println("打印中") }() defer func() { fmt.Println("打印后") }() panic("触发异常") fmt.Println("test") }
-------output-------
打印后
打印中
打印前
panic: 触发异常 goroutine 1 [running]:
main.test()
D:/Go_Path/go/src/logDemo/main.go:15 +0x98
main.main() D:/Go_Path/go/src/logDemo/main.go:8 +0x27
exit status 2
三. recover 用法
recover也是golang的一个内建函数, 其实就是try catch。
不过需要注意的是:
1. recover如果想起作用的话, 必须在defered函数中使用。
2. 在正常函数执行过程中,调用recover没有任何作用, 他会返回nil。如这样:fmt.Println(recover()) 。
3. 如果当前的goroutine panic了,那么recover将会捕获这个panic的值,并且让程序正常执行下去。不会让程序crash。
func main() { fmt.Println("c") defer func() { // 必须要先声明defer,否则不能捕获到panic异常 fmt.Println("d") if err := recover(); err != nil { fmt.Println(err) // 这里的err其实就是panic传入的内容 } fmt.Println("e") }() f() //开始调用f fmt.Println("f") //这里开始下面代码不会再执行 } func f() { fmt.Println("a") panic("异常信息") fmt.Println("b") //这里开始下面代码不会再执行 }
-------output------- c a d 异常信息 e
参考链接
1. defer关键字
2. Golang中defer、return、返回值之间执行顺序的坑
作者:章为忠
如有问题,可以微信:18618243664 联系我,非常感谢。
关注我的微信公众号,获取相关的 源代码及视频资料。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?