Go 语言没有像 Java 和 .NET 那样的 try/catch
异常机制:不能执行抛异常操作。但是有一套 defer-panic-and-recover
机制。
Golang中引入两个内置函数panic和recover来触发和终止异常处理流程,同时引入关键字defer来延迟执行defer后面的函数。一直等到包含defer语句的函数执行完毕时,延迟函数(defer后的函数)才会被执行,而不管包含defer语句的函数是通过return的正常结束,还是由于panic导致的异常结束。你可以在一个函数中执行多条defer语句,它们的执行顺序与声明顺序相反。
当发生像数组下标越界或类型断言失败这样的运行错误时,Go 运行时会触发运行时panic
,伴随着程序的崩溃抛出一个runtime.Error
接口类型的值。这个错误值有个RuntimeError()
方法用于区别普通错误。
panic 可以直接从代码初始化:当错误条件(我们所测试的代码)很严苛且不可恢复,程序不能继续运行时,可以使用 panic 函数产生一个中止程序的运行时错误。panic
接收一个做任意类型的参数,通常是字符串,在程序死亡时被打印出来。Go 运行时负责中止程序并给出调试信息。
panic()
是一个内建函数,可以中断原有的控制流程,进入一个令人panic
(恐慌即Java中的异常)的流程中。当函数调用panic
时,函数的执行会被中断,最简单的panic
示例代码如下:
package main
import "fmt"
func main() {
fmt.Println("Starting the program")
panic("A severe error occurred: stopping the program!")
fmt.Println("Ending the program")
}
这段代码的打印结果因人而异,笔者win10 64位+go1.7.5的环境下打印结果如下:
Starting the program
panic: A severe error occurred: stopping the program!
goroutine 1 [running]:
panic(0x4951e0, 0xc0420062c0)
D:/Go/src/runtime/panic.go:500 +0x1af
main.main()
E:/testcode/test.go:7 +0xf6
exit status 2
注意:一定要记住,应当把panic
作为最后的手段来使用,也就是说,你的代码中应当没有或者很少有panic
的东西。这是个强大的工具,请明智谨慎地使用它。
recover
是一个内建的函数,可以让进入令人恐慌的流程中的goroutine恢复过来。recover
仅在延迟函数中有效。在正常的执行过程中,调用recover
会返回nil
,并且没有其它任何效果。如果当前的goroutine
陷入panic
,调用recover
可以捕获到panic
的输入值,并且恢复正常的执行。
一般情况下, recover()
应该在一个使用defer
关键字的函数中执行以有效截取错误处理流程。如果没有在发生异常的goroutine
中明确调用恢复过程(使用recover
关键字),会导致该goroutine
所属的进程打印异常信息后直接退出。
从程序栈的角度来讲,panic
会导致栈被展开直到defer
修饰的 recover()
被调用或者程序中止。
一个展示panic
,defer
和 recover
怎么结合使用的完整例子如下:
package main
import (
"fmt"
)
func badCall() {
panic("bad end")
}
func test() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("Panicing %s\n", e)
}
}()
badCall()
fmt.Printf("After bad call\n")
}
func main() {
fmt.Printf("Calling test\n")
test()
fmt.Printf("Test completed\n")
}
//output
/*
Calling test
Panicing bad end
Test completed
*/
Go 语言的错误处理流程:当一个函数在执行过程中出现了异常或遇到 panic()
,正常语句就会立即终止,然后执行defer
语句,再报告异常信息,最后退出 goroutine
。如果在defer
中使用了 recover()
函数,则会捕获错误信息,使该错误信息终止报告。
这里结合上一关卡所学的error
内容,实现一个自定义的Error()
方法,并结合panic
和recover
,集中展示下Go
语言的错误处理机制:
package main
import (
"fmt"
)
//自定义错误类型
type ArithmeticError struct {
error
}
//重写Error()方法
func (this *ArithmeticError) Error() string {
return "自定义的error,error名称为算数不合法"
}
//定义除法运算函数***这里与本文中第一幕①error接口的例子不同
func Devide(num1, num2 int) int {
if num2 == 0 {
panic(&ArithmeticError{}) //当然也可以使用ArithmeticError{}同时recover等到ArithmeticError类型
} else {
return num1 / num2
}
}
func main() {
var a, b int
fmt.Scanf("%d %d", &a, &b)
defer func() {
if r := recover(); r != nil {
fmt.Printf("panic的内容%v\n", r)
if _, ok := r.(error); ok {
fmt.Println("panic--recover()得到的是error类型")
}
if _, ok := r.(*ArithmeticError); ok {
fmt.Println("panic--recover()得到的是ArithmeticError类型")
}
if _, ok := r.(string); ok {
fmt.Println("panic--recover()得到的是string类型")
}
}
}()
rs := Devide(a, b)
fmt.Println("结果是:", rs)
}
用 go run file.go执行该文件,输入5 2得:
$ go run test.go
5 2
结果是: 2
输入5 0得:
$ go run test.go
5 0
panic的内容自定义的error,error名称为算数不合法
panic--recover()得到的是error类型
panic--recover()得到的是ArithmeticError类型