吐糟点

Golang这么时尚的语言是没有类似try..catch 这种异常处理机制,而是使用 panic 和 recover处理异常. 其实相当于python的raise

介绍

golang的异常处理组合 panic,defer,recover,跟java中的try catch finially是类似的。 但是从语言的用户体验来说,不怎么好。 但考虑到golang的场景基本是系统高性能层面的,这种精准错误处理应该减少那种后遗症bug

使用panic抛出异常,抛出异常后将立即停止当前函数的执行并运行所有被defer的函数,然后将panic抛向上一层,直至程序carsh。但是也可以使用被defer的recover函数来捕获异常阻止程序的崩溃,recover只有被defer后才是有意义的

注意点

1、defer 需要放在 panic 之前定义,另外recover只有在 defer 调用的函数中才有效。
2、recover处理异常后,逻辑并不会恢复到 panic 那个点去,函数跑到 defer 之后的那个点.
3、多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用

panic (主动爆出异常) 与 recover (收集异常)

1、recover 用来对panic的异常进行捕获. panic 用于向上传递异常,执行顺序是在 defer 之后

func f() {
    for {
        fmt.Println("1")
        a := []string{"a","b"}
        fmt.Println(a[3])  // 这里slice越界异常了
        /*panic("bug")*/
        fmt.Println("4")
        time.Sleep(1 * time.Second)
    }
}

2、如果你不把这个异常panic recover处理的化,那么就会发生下面的情况.

1
panic: runtime error: index out of range

goroutine 1 [running]:
panic(0xda9c0, 0x8201c8090)
    /usr/local/go/src/runtime/panic.go:464 +0x3e6
main.f()
    /Users/ruifengyun/gg.go:23 +0x33b
main.main()
    /Users/ruifengyun/gg.go:16 +0x14
exit status 2

3、处理panic的例子.

package main

import (
    "fmt"
)

func main() {
    defer func() {
        fmt.Println("2")
        if err := recover(); err != nil {
            fmt.Println(err)
        }
        fmt.Println("3")
    }()
    test()
}

func test() {
    fmt.Println("1")
    panic("bug")
    fmt.Println("4")
}

4、那么上面代码的运行结果是:

1
    2
    bug
    3

5、上面go代码实例中,异常是我们通过panic方法主动抛出来的,但如果真的就出现了未知的异常咋办?

package main

#http://xiaorui.cc

import (
    "fmt"
    "time"
)

func main() {
    f()
    fmt.Println("end")
}

func f() {
    defer func() { //必须要先声明defer,否则不能捕获到panic异常
        fmt.Println("xiaorui.cc start")
        if err := recover(); err != nil {
            fmt.Println(err) //这里的err其实就是panic传入的内容,"bug"
        }
        fmt.Println("xiaorui.cc end")
    }()
    for {
        fmt.Println("1")
        a := []string{"a", "b"}
        fmt.Println(a[3])  // 越界访问,肯定出现异常
        panic("bug”)  // 上面已经出现异常了,所以肯定走不到这里了。 
        fmt.Println("4") //不会运行的.
        time.Sleep(1 * time.Second)
    }
}

运行结果如下:

1
xiaorui.cc start
runtime error: index out of range
xiaorui.cc end
end

总结

我们可以看到出现的异常会走到defer这一步,defer这里可以打印具体的异常信息,defer运行完之后不能回到原点,控制权会被扔到该函数的外层,也就是调用这个函数的层,对应上面的代码也就是main()函数