Go语言入门-异常处理-pannic和recover

Go语言入门-异常处理-pannic和recover

简述

Go语言try…catch结构化的异常机制,但是可以使用panic进行错误的抛出,使用recover来进行异常恢复处理。

基础语法

func panic(interface{})
func recover() interface{}

使用场景

Go语言函数中可以抛出一个运行时恐慌后,然后通过defer使用recover来处理运行时恐慌。panic和recover是内置函数。而不是语句。由于panic参数是空接口类型,因此可使用任何类型作为错误状态。而recover返回的结果同样要做转型才能获取具体信息。
来个例子吧

  • 示例1
    panic中断了当前main函数执行
func main() {
    panic("I will deal")
}
/**
ouput:
goroutine 1 [running]:
main.main()
	~/panic.go:4 +0x40
*/
  • 示例2
    panic中断了当前main函数执行,然后使用recover进行异常恢复处理–错误示范
func main() {
    panic("I will deal")
    defer func() {
        fmt.Println(recover())
    }()
}
/**
ouput:
goroutine 1 [running]:
main.main()
	~/panic.go:4 +0x40
*/

哎为 啥执行还是异常了,不是说把recover放在defer里面就可以了吗?不好意思除了这个限制外还有一个限制:

defer 必须放在 panic 之前定义

  • 示例3
    panic中断了当前main函数执行,然后使用recover进行异常恢复处理–正确示范
func main() {
    //defer放在panic前面
    defer func() {
        fmt.Println(recover())
    }()
    //我要panic了
    panic("I will deal")
}
/**
output:
I will deal
 */
  • 示例4
    多次panic演示,多次panic只捕获第一panic的,不会recover以后又跑搭配panic后续的处理逻辑。

那么是不是在一个函数中只能引发以后panic呢 ?

func main() {
    defer func() {
        fmt.Println(recover())
    }()
    
    panic("I will deal1") //只会执行一次因为当前panic已经中断掉main,被defer函数捕获后直接退出main函数不在执行panic后续处理逻辑
    panic("I will deal2")
}
/**
output:
I will deal1
 */
  • 示例5
    在defer语句中panic的场景,覆盖掉普通函数中的panic
func main() {
    defer func() {
        fmt.Println(recover())//recover的竟然是defer语句的panic?
    }()
    defer func() {
        panic("I will deal3") 
    }()
    panic("I will deal1") //此行代码是否执行了?
    panic("I will deal2")
}
/**
output:
I will deal3
 */

以上案例中发现recover捕获的最后执行的pannic,按照程序执行的逻辑,panic(“I will deal1”)应该是执行后出发defer语句中的 panic(“I will deal3”) 然后最后一个defer
recover捕获了defer语句的中panic

意味着panic在一个函数中是可以执行多次,但是在正常的处理逻辑只会执行一次,当defer语句引发panic后,普通函数中的panic会被覆盖。因此可以再defer语句先恢复recover 运行时恐慌panic再次抛出运行时恐慌。

  • 示例6
    在defer语句中panic的场景中先恢复panic再抛出panic
func main() {
    defer func() {
        fmt.Println(recover())//恢复最后一次panic
    }()
    defer func() {
        fmt.Println(recover()) //恢复普通函数的panic
        panic("I will deal3")
    }()
    panic("I will deal1") //此行代码是否执行了?
    panic("I will deal2")
}
/**
output:
I will deal1
I will deal3

 */

以上例子中可以看出 panic可以多次执行,但是通过实例5的比较可以得出以下结论。

当多次panic抛出以后如果没有recover掉,panic会发生覆盖,最后recover如果恢复或者捕获的话,只会捕获到最后一个没有被捕获的panic。

  • 示例7
    recover的使用范围,只能放在defer延迟调用的函数中直接调用,其他花里胡哨的写法都只能拿到nil。

func testRecover() {
    defer fmt.Println("defer ", recover())
    fmt.Println("test recover 1", recover())
    defer func() {
        func() {
            fmt.Println("defer func inner 1", recover())
        }()
        //捕获recover
        fmt.Println("defer func ", recover())
        func() {
            fmt.Println("defer func inner 2", recover())
        }()
    }()
    defer fmt.Println(recover())
    fmt.Println("test recover 2", recover())
    panic("call panic")
    fmt.Println("test recover 3", recover())
}
func main() {
    testRecover()
}
/**
output:
test recover 1 <nil>
test recover 2 <nil>
<nil>
defer func inner 1 <nil>
defer func  call panic
defer func inner 2 <nil>
defer  <nil>
 */

1. recover必须在defer调用中直接使用
2. defer必须要在pannic之前

  • 示例8
    panic捕获后继续处理程序(保护代码段)
func recover1( a, b int) {
    result := 0
    //result := 19/0
    //fmt.Println(result)
    //.\panic.go:121:17: division by zero
    func() {
        defer func() {
            if recover() != nil {
                result = 0
                fmt.Println("ReSet result=[0]")
            }
        }()
        if result == 0 {
            panic("division by zero")
        }
        result = a/b
        fmt.Println(result)
    }()
}

func main() {
    recover1(10, 0)
}
/**
output:
ReSet result=[0]
 */

使用匿名函数的作用域用来保证某一段代码能够一直顺序执行,保证函数不被中断掉。

  • 示例9
    模拟try…catch todo
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章