简言

在其他语言里,宕机往往以异常的形式存在,底层抛出异常,上层逻辑通过 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捕获这个异常,然后正常处理,从而恢复正常代码的执行

实验如下图:

go 异常捕获处理 panic defer recover_go异常恢复

实验代码如下(已添加详细注释,不再一一详述):

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()
}