GO-异常处理

异常处理1:恐慌与处理

恐慌概述

  • 代码在运行时如果出现异常,系统会报出恐慌(panic)并终止运行
  • IDE和终端打印的恐慌日志,包含了恐慌的信息以及报恐慌所在的代码行
  • 恐慌好比一种震撼的暴力教育,其目的在于警示开发者,什么是可以的而什么又是不可以的
  • 代码在交付使用前要经过充分测试,处理一切可能的恐慌

系统报恐慌 
本例中由于我们错误地使用了一个超出数组长度的下标,导致系统报出恐慌

func demo21() {
    a := [5]int{0, 1, 2, 3, 4}

    a[1] = 123
    index := 2 + 8

    //系统报恐慌:运行时错误(exe在执行的过程中发生的错误),下标越界
    //panic: runtime error: index out of range
    a[index] = 123
}

自己报恐慌

  • 除了系统报出恐慌以外,我们还可以通过内建函数panic自己报出恐慌
  • 自己报恐慌的目的,是预测程序在运行时可能出现的异常情形,并提示当前代码的调用者以错误信息
  • 下面的例子中,计算圆面积这一函数在调用时如果传入了一个负数的半径,则会报出恐慌,提示半径是不可以为负数的,这样做相当于强制代码的调用者传入非负的半径
func getCircleArea(radius float32) (area float32) {
    //如果半径参数为负数,则抛出异常
    if radius < 0{
        panic("颤抖吧,您的智商已下线,半径不能为负数")
    }
    return 3.14 * radius * radius
}
func demo22() {
    //程序会因为抛出的异常未经处理而崩溃
    getCircleArea(-5)
}

处理恐慌

  • 程序在上线前必须测试和扫灭所有可能的恐慌
  • 在没有对恐慌进行任何处理前,程序会在报恐慌的行崩溃
  • Go语言提供了recover內建函数,让崩溃的程序复活并返回造成程序崩溃的error实例
  • 所以我们可以在正式的业务逻辑开始前,事先挂起一个延时处理恐慌的函数,在其中借助recover函数获得造成程序崩溃的error并处理
  • 当程序在123行崩溃时,123行以后的代码就不会再执行了,而是直接跳转到defer了的恐慌处理程序
func demo23() {

    //延时执行恐慌处理程序
    //延时到什么时候?①函数正常结束前②恐慌发生时(函数内恐慌以后的代码将不会执行)
    defer func() {
        if err := recover(); err != nil {
            fmt.Println(err)
        }
    }()

    //下面的函数会恐慌(报恐慌)
    getCircleArea(-5)

    //所以这里执行不到,而是直接跳到defer所定义的恐慌处理程序
    print("这里有美女相赠")
}
  • 下面这段代码对前面的demo23进行了调用
  • 由于demo23()是一个已经定义了恐慌处理方式的函数,程序不会因为demo23中的恐慌而崩溃
func demo24() {
    //本来该应该因恐慌而死,但已经在其中插入了异常(恐慌)处理程序,就不会造成程序崩溃
    //丢失的部分仅仅为demo23中恐慌以后的部分
    demo23()

    fmt.Println("抢钱抢粮抢地盘")
    fmt.Println("GAME OVER")
}

异常处理2:以返回错误替代恐慌

概述

  • 通过恐慌报错的方式虽然直白有效,但动不动就崩溃显得有些暴力
  • Go语言还给我们提供了一种相对温和但同样有效的异常解决方案,那就是同时返回结果和错误(error实例)
  • 如果结果正确时错误为空,如果错误不为空时结果为空(或没有业务意义的默认值)
  • 这种方式显得温和而辩证,兼容性好,也不会造成程序崩溃
  • 至于究竟是严厉好还是温和兼容好,开发者们可以见仁见智

下面实例中的圆面积计算函数中:

  • 如果调用者传入了一个负数半径,程序也不会panic,但是会返回一个提示错误的error实例,此时结果是毫无意义的默认值0
  • 如果调用者传入了合法的半径,则返回正确结果和一个为空的error实例
package main

import (
    "errors"
    "fmt"
)

func main() {
    //获得调用的结果
    ret, err := getCircleAreaII(5)

    //如果返回的错误不为空,则说明程序出现了异常
    if err!=nil{
        fmt.Println(err)
    }else {
        fmt.Println("ret=",ret)
    }
}

/*
如果发生异常时,以返回错误的方式代替恐慌
正确结果和错误,必有一个为空
这样相对温和,也不会造成程序崩溃
*/
func getCircleAreaII(radius float32)(ret float32,err error) {
    if radius < 0{
        err = errors.New("傻鸟,半径不能为负")
        return
    }
    ret = 3.14 * radius * radius
    return
}

异常处理3:自定义异常

自定义异常概述

  • 系统提供的异常类error无所不包但控制的粒度很粗糙
  • 如果想要进行很精细化的报错和处理错误,就需要用到自定义异常
  • 我们只要实现error接口中的Error() string方法,就可以自定义异常了
  • 自定义的异常与系统异常无二,同样可以panic,同样可以辩证互斥地返回【结果错误对】

自定义异常

  • 下面的例子定义了一个【非法参数异常】
  • 在这个自定义的结构体中,封装了异常提示信息、发生时间、造成异常的用户三个属性
  • 这样的异常在报出和处理时,无疑具有更直观更丰富的参考价值
//自定义【非法参数异常】,封装异常提示信息、发生时间、造成异常的用户三个属性
type InvalidArgError struct {
    info string
    when time.Time
    user string
}

//实现系统的异常接口
func (iae *InvalidArgError)Error() string{
    return fmt.Sprintln(iae.info,iae.when,iae.user)
}

//模仿SDK,提供一个创建异常对象的工厂方法,自动记录发生异常的时间和用户
func NewInvalidArgError(info string) *InvalidArgError {
    iaePtr := new(InvalidArgError)
    iaePtr.info = info
    iaePtr.when = time.Now()
    iaePtr.user = "bill"
    return iaePtr
}

以错误的形式返回异常信息

//如果参数为负数时,返回一个自定义的异常
func getCircleAreaIII(radius float32)(ret float32,err interface{}) {
    if radius < 0{
        //err = errors.New("傻鸟,半径不能为负")
        err = NewInvalidArgError("傻鸟,半径不能为负")
        return
    }
    ret = 3.14 * radius * radius
    return
}

//返回自定义异常
func demo41() {
    ret, err := getCircleAreaIII(-5)
    if err != nil {
        //fmt.Println(err)
        fmt.Sprintln(err)
    } else {
        fmt.Println("ret=", ret)
    }
}

func main() {
    demo41()
}

以恐慌的形式报异常并处理

//半径为负数的情况下以恐慌形式报出异常
func getCircleAreaIV(radius float32)(ret float32) {
    if radius < 0{
        panic(NewInvalidArgError("傻吊半径不能为负数"))
    }
    ret = 3.14 * radius * radius
    return
}

//处理抛出的恐慌
func demo42() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println(err)
        }
    }()
    getCircleAreaIV(-5)
}

func main() {
    demo42()
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值