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
*

  

posted @   专职  阅读(546)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示