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