golang之defer、panic、recover

go中可以抛出一个panic的异常,然后在defer中通过recover捕获异常,然后生成终止函数(程序)

defer介绍

defer语法介绍

1.defer函数的参数被计算在defer被声明的的时候。

func a() {
    i := 0
    defer fmt.Println(i)
    i++
    return
}
//结果: 打印的i值为0,因为i值为0的时候,使用了defer关键字

2.defer函数的执行按照后进先出 的顺序。

func b() {
  for i := 0; i < 4; i++ {
    defer fmt.Print(i)
  }
}
//结果: “3210”,(后进先出的顺序,无需解释)

3.defer函数可以读取和分配返回函数的返回值(Deferred functions may read and assign to the returning function’s named return values)。

func c() (i int) {
    defer func() { i++ }()
    return 1
}
//结果: 函数返回值为2
//原因:return 1会将函数返回值i赋值为1, 然后执行i++,所以返回值为2
//这里出一道结合了1,3两个用法的题。
func c() (i int) {
  defer func(i int) {
    i++
    fmt.Println("i's value ", i)
  }(i)
  return 1
}
//结果:函数的返回值为1,打印内容为“i's value 1"
//原因:defer函数在定义的时候就确定参数的值

defer的执行次序

defer、return、返回值三者的执行逻辑应该是:return最先执行,return负责将结果写入返回值;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出。

1.无名返回值的情况

package main

import (
    "fmt"
)

func main() {
    fmt.Println("return:", a()) // 打印结果为 return: 0
}

func a() int {
    var i int
    defer func() {
        i++
        fmt.Println("defer2:", i) // 打印结果为 defer: 2
    }()
    defer func() {
        i++
        fmt.Println("defer1:", i) // 打印结果为 defer: 1
    }()
    return i
}
//原因:函数的返回值没有被提前声名,其值来自于其他变量的赋值,而defer中修改的也是其他变量,而非返回值本身,因此函数退出时返回值并没有被改变。

2.有名返回值的情况

package main

import (
    "fmt"
)

func main() {
    fmt.Println("return:", b()) // 打印结果为 return: 2
}

func b() (i int) {
    defer func() {
        i++
        fmt.Println("defer2:", i) // 打印结果为 defer: 2
    }()
    defer func() {
        i++
        fmt.Println("defer1:", i) // 打印结果为 defer: 1
    }()
    return i // 或者直接 return 效果相同
}
//原因:函数的返回值被提前声名,也就意味着defer中是可以调用到真实返回值的,因此defer在return赋值返回值 i 之后,再一次地修改了 i 的值,最终函数退出后的返回值才会是defer修改过的值。

3.返回指针的情况

package main

import (
    "fmt"
)

func main() {
    fmt.Println("c return:", *(c())) // 打印结果为 c return: 2
}

func c() *int {
    var i int
    defer func() {
        i++
        fmt.Println("c defer2:", i) // 打印结果为 c defer: 2
    }()
    defer func() {
        i++
        fmt.Println("c defer1:", i) // 打印结果为 c defer: 1
    }()
    return &i
}
//原因:c()*int 的返回值是指针变量,那么在return将变量 i 的地址赋给返回值后,defer再次修改了 i 在内存中的实际值,因此函数退出时返回值虽然依旧是原来的指针地址,但是其指向的内存实际值已经被成功修改了。

panic

panic是用来表示非常严重的不可恢复的错误的。在Go语言中这是一个内置函数,接收一个interface{}类型的值作为参数。如果不使用recover,panic会导致程序直接挂掉。关键的一点是,在函数执行panic的时候,函数会首先执行defer定义的内容,在defer内容执行完之后,panic再向上传递。

recover

recover也是一个Go语言的内置函数,用于再次恢复panic的goroutine。recover必须在defer函数中使用 。当正常执行的是时候,调用recover将会返回nil并且不会有任何其他影响;如果当前的goroutine是panic,调用recover将捕捉到panic传入的值并且再次正常执行。

注意: recover之后,逻辑并不会恢复到panic那个点去,函数还是会在defer之后返回。

综合例子展示

package main

import "fmt"

func main() {
    f()
    fmt.Println("Returned normally from f.")
}

func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Calling g.")
    g(0)
    fmt.Println("Returned normally from g.")
}

func g(i int) {
    if i > 3 {
        fmt.Println("Panicking!")
        panic(fmt.Sprintf("%v", i))
    }
    defer fmt.Println("Defer in g", i)
    fmt.Println("Printing in g", i)
    g(i + 1)
}
结果
Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.

此处不做解释,如果不懂,返回查看前文描述的内容即可。

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值