golang中的异常处理
1. defer是go提供的一种资源处理的方式。defer的用法遵循3个原则在defer表达式被运算的同时,defer函数的参数也会被运算。如下defer的表达式println运算的同时,其入参i也会被运算,结果为初始化值0,故defer调用中会打印“0”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package main import "fmt" func main() { f() } func f() { i := 0 defer fmt.Println( "defer one i" , i) // defer three i 0 i++ defer fmt.Println( "defer two i" , i) // defer three i 1 i++ fmt.Println( "i" , i) // i 2 defer fmt.Println( "defer three i" , i) // defer three i 2 } // 输出结果: /* i 2 defer three i 2 defer two i 1 defer one i 0 */ |
2. defer函数在一个函数return之后遵循后进先出的调用原则,如下打印结果为43210
func b() {
for i := 0; i < 5; i++ {
defer fmt.Print(i) // 打印 43210
}
}
3. defer函数可能会读取并赋值给所在函数的返回值,如下返回值为2
1 2 3 4 | func c() (i int) { defer func () { i++ }() return 1 } |
针对上述的第三点有如下三种情况:分别返回 1 5 1 5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | // defer案例1 func f1() (result int) { defer func () { result++ }() fmt.Println() return 0 } /* 等价于 func f1() (result int) { result = 0 // return语句不是一条原子调用,return xxx其实是赋值+return指令 defer func() { result++ }() return // 空的return指令 } */ // defer案例2 func f2() (r int) { t := 5 defer func () { t = t + 5 }() return t } /* 等价于 func f2() (r int) { t := 5 r = t // 赋值指令 defer func() { // defer被插入到赋值与返回之间执行,这个例子中返回值r没被修改过 t = t + 5 }() return // 空的return指令 } */ // defer案例3 func f3() (r int) { defer func (r int) { r = r + 5 }(r) return 1 } /* 等价于 func f3() (r int) { r = 1 // 给返回值赋值 defer func(r int) { // 这里改的r是传值传进去的r,不会改变要返回的那个r值 r = r + 5 }(r) return // 空的return指令 } */ <br><br> // defer案例4 <br>func f4() (r int) {<br> t := 5<br> defer func() {<br> t += 5<br> }()<br> r += 3<br> return t // 返回5<br>}<br>/*<br>func f4() (r int) {<br> t := 5<br> defer func() {<br> t = t + 5<br> }()<br> r += 3<br> r = t<br> return // 空的return指令 返回5<br>}<br>*/ |
4. panic和recover的使用需要遵循以下原则:
1 2 3 | defer 需要放在 panic 之前定义,另外recover只有在 defer 调用的函数中才有效。 recover处理异常后,逻辑并不会恢复到 panic 那个点去,函数跑到 defer 之后的那个点. 多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | package main import ( "fmt" ) func main() { f() fmt.Println( "end" ) } func f() { defer func () { // 必须要先声明defer,否则不能捕获到panic异常 fmt.Println( "defer start" ) if err := recover(); err != nil { fmt.Println(err) // 这里的err其实就是panic传入的内容,"runtime error: index out of range [3] with length 2" } fmt.Println( "defer end" ) }() for { fmt.Println( "func begin" ) a := []string{ "a" , "b" } fmt.Println(a[3]) // 越界访问,肯定出现异常 //panic("bug") // 上面已经出现异常了,所以肯定走不到这里了。 fmt.Println( "func end" ) // 不会运行的. } } |
输出结果:
1 func begin
2 defer start
3 runtime error: index out of range
4 defer end
5 end
参考网址: https://studygolang.com/articles/13630
5. 多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | package main import ( "fmt" ) /* defer 需要放在 panic 之前定义,另外recover只有在 defer 调用的函数中才有效。 recover处理异常后,逻辑并不会恢复到 panic 那个点去,函数跑到 defer 之后的那个点. 多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用 */ func main() { f2() fmt.Println( "end" ) } func f2() { // 必须要先声明defer,否则不能捕获到panic异常 defer func () { fmt.Println( "defer 开始了" ) if err := recover(); err != nil { // 这里的err其实就是panic传入的内容,"runtime error: index out of range [3] with length 2" fmt.Println(err) } fmt.Println( "defer 结束了" ) }() defer func () { fmt.Println( "defer2 开始了" ) if err := recover(); err != nil { fmt.Println(err) } fmt.Println( "defer2 结束了" ) }() fmt.Println( "func 开始了" ) s1 := []string{ "18" , "我" } fmt.Println(s1[2]) // 越界访问,肯定出现异常 panic( "BUG" ) // 上面已经出现异常了,所以肯定走不到这里了。 fmt.Println( "func 结束了" ) // 不会运行的. } // 输出结果 /* func 开始了 defer2 开始了 runtime error: index out of range [2] with length 2 defer2 结束了 defer 开始了 defer 结束了 en * |
分类:
golang
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)