简言
在其他语言里,宕机往往以异常的形式存在,底层抛出异常,上层逻辑通过 try/catch 机制捕获异常,没有被捕获的严重异常会导致宕机
go语言追求简洁,优雅,Go语言不支持传统的 try…catch…finally 这种异常
Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱
Go语言,可以使用多值返回来返回错误。不要用异常代替错误,更不要用来控制流程。在极个别的情况下,才使用Go中引入的Exception处理:defer, panic, recover
Go中,对异常处理的原则是:多用error包,少用panic
panic() 函数
函数中遇到panic语句,会立即终止当前函数的执行,在panic所在函数内如果存在要执行的defer函数列表,按照defer的逆序执行
recover() 函数
recover函数的返回值报告协程是否正在遭遇panic
有异常时,recover()只能调用一次,后面再次调用则捕获不到任何异常
通常办法:go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理,从而恢复正常代码的执行
实验如下图:
实验代码如下(已添加详细注释,不再一一详述):
package main import ( "fmt" "runtime/debug" ) // 异常处理函数1 func panicDeal1() { fmt.Println("panicDeal1,begin") if err := recover(); err != nil { fmt.Println("err1:", err) // 打印出异常(由于panicDeal2()中的recover函数已经捕获了异常,所以这里捕获不到异常,不会得到执行) fmt.Println(string(debug.Stack())) // 打印出堆栈信息 } fmt.Println("panicDeal1,end") } // 异常处理函数2 func panicDeal2() { fmt.Println("panicDeal2,begin") if err := recover(); err != nil { fmt.Println("err2", err) // 打印出异常 fmt.Println(string(debug.Stack())) // 打印出堆栈 } fmt.Println("panicDeal2,end") } func test() { fmt.Println("1111") // 必须先声明defer,否则不能捕获panic异常 defer panicDeal1() // 触发panic时,逆序执行,也就是先执行 panicDeal2(),再执行 panicDeal1() defer panicDeal2() fmt.Println("2222") // 空指针赋值,产生崩溃 var p *int *p = 1 // 这里的代码得不到执行 fmt.Println("3333") } func main() { test() }