• 【Hello Go】Go语言异常处理


    异常处理

    error接口

    Go语言引入了一个关于错误处理的标准模式 它是Go语言内建的接口类型 它的定义如下

    type error interface {
      Error() string
    }
    
    • 1
    • 2
    • 3

    Go语言的标准库代码包errors为用户提供了以下方法

    package errors
    type errorString struct {
    text string
    }
    
    func New(text string) error {
    return &errorString{text}
    }
    
    func (e *errorString) Error() string {
    return e.text
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    另一个可以生成 error 类型值的方法是调用 fmt 包中的 Errorf 函数:

    package fmt
    import "errors"
    func Errorf(format string, args ...interface{}) error {
    return errors.New(Sprintf(format, args...))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    下面是代码示例

    import (
    "errors"
    "fmt"
    )
    func main() {
    var err1 error = errors.New("a normal err1")
    fmt.Println(err1) //a normal err1
    var err2 error = fmt.Errorf("%s", "a normal err2")
    fmt.Println(err2) //a normal err2
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    一般来说 错误处理位于函数的最后 下面是代码示例

    import (
    "errors"
    "fmt"
    )
    
    func Divide(a, b float64) (result float64, err error) {
    if b == 0 {
    result = 0.0
    err = errors.New("runtime error: divide by zero")
    return
    }
    result = a / b
    err = nil
    return
    }
    func main() {
    r, err := Divide(10.0, 0)
    if err != nil {
    fmt.Println(err) //错误处理 runtime error: divide by zero
    } else {
    fmt.Println(r) // 使用返回值
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    panic

    通常情况下 我们报错时可以返回一个error值

    但是如果我们遇到了不可恢复的异常 比如说数组越界访问 空指针引用等

    此时就会引发panic异常


    一般而言 当当panic异常发生的时候 该程序会立刻停止运行 并且立即执行在该goroutine(我们这里暂时理解为线程)下的defer的函数

    随后 程序崩溃并输出日志信息 日志信息包括panic value和函数调用的堆栈跟踪信息

    此外需要注意的是 并不是所有panic异常都来自运行时 我们主动调用panic函数也会引起panic异常

    panic函数可以接受任何值作为参数

    func panic(v interface{})
    
    • 1

    下面是代码和结果演示

    package main
    
    import "fmt"
    
    func testA() {
    	fmt.Println("testA ~~~")
    }
    
    func testB() {
    	panic("panic error ~~~")
    }
    
    func testC() {
    	fmt.Println("testC ~~~")
    }
    
    func main() {
    	testA()
    	testB()
    	testC()
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    我们发现前面的函数顺序执行完毕之后到panic异常

    当然如果我们自己主动写点bug比如说数组越界 除0错误 效果也是一样的

    recover

    程序运行时 只要引发了panic异常就会引发程序崩溃 这是我们怎么都不愿意看到的

    因此Go语言给我们提供了一种专门拦截运行时异常的内建函数 – recover

    它可以使程序从panic状态恢复并获得流程控制权

    注意:!!! recover只有在defer调用的函数中有效

    如果说调用了内置函数decover并且定义该defer语句的函数发生了panic异常 recover会从panic中恢复 并且返回panic和vlaue 导致panic异常的函数不会正常运行 但会正常返回

    但是在未发生panic时调用recover recover会返回nil

    代码演示如下

    package main
    
    import "fmt"
    
    func testA() {
    	fmt.Println("testA ~~~")
    }
    
    func testB() (err error) {
    
    	defer func() {
    		if x := recover(); x != nil {
    			// 此时panic和value被添加到x中
    			err = fmt.Errorf("internal error: %v", x)
    		}
    	}()
    	panic("panic error ~~~")
    
    	return err
    }
    
    func testC() {
    	fmt.Println("testC ~~~")
    }
    
    func main() {
    	testA()
    	err := testB()
    	fmt.Println(err)
    	testC()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    我们依旧是在test2中主动调用了panic异常

    只不过我们在前面延时调用了一个匿名函数

    	defer func() {
    		if x := recover(); x != nil {
    			// 此时panic和value被添加到x中
    			err = fmt.Errorf("internal error: %v", x)
    		}
    	}()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这个匿名函数中我们调用了recover函数 并且接受了err

    最终我们可以发现我们的程序正常运行下去了

    在这里插入图片描述

    延时调用错误问题

    如果我们在延时调用中也引发了panic异常 那么该panic异常可以被后续延时调用的recover捕获

    代码和结果演示如下

    func test() {
    	defer func() {
    		fmt.Println(recover())
    	}()
    
    	defer func() {
    		panic("test2")
    	}()
    
    	panic("test1")
    }
    
    func main() {
    	test()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    此时我们的recover捕获的错误就不是test1 而是 test2

  • 相关阅读:
    【数据库07】后端开发必备的大数据知识指南
    多路彩灯控制器led流水灯VHDL速度可调仿真图视频、源代码
    单例模式c++
    2024字节跳动校招面试真题汇总及其解答(四)
    1899. 合并若干三元组以形成目标三元组-c语言-贪心算法+标记数组
    [1172]python操作odps
    我的三周年创作纪念日
    C语言适不适合新手学习?
    Ubuntu18安装Hadoop,zookeeper,hbase--记录
    Android ImageView视图的七种图片缩放类型
  • 原文地址:https://blog.csdn.net/meihaoshy/article/details/134486373