Golang 错误和异常

本文探讨了Go语言中的错误处理机制,包括Error接口的使用、自定义error的创建、以及Recover和Panic的原理和应用。重点介绍了如何捕获和处理异常,以及如何在Web框架中使用Recover进行宕机恢复。
摘要由CSDN通过智能技术生成

(一)基础

(1)认识

错误处理是每个编程语言都要考虑的一个重要话题。在Go语言的错误处理中,错误 是软件包API和应用程序用户界面的一个重要组成部分。
Go 语言中的错误处理与其他语言不太一样,它把错误当成一种值来处理,更强调判断错误、处理错误,而不是一股脑的 catch 捕获异常。Go 语言中把错误当成一种特殊的值来处理,不支持其他语言中使用try/catch捕获异常的方式。

(2)Error 接口

Error 接口定义如下,其中只有一个Error()方法返回值为字符串,返回一个描述错误信息的字符串。当一个函数或方法需要返回错误时,我们通常是把错误作为最后一个返回值。

type error interface {
	Error() string
}

由于 error 是一个接口类型,默认零值为nil。当返回的错误值不是 nil 时,我们 可以通过调用 error 接口类型的 Error 方法来获得字符串类型的错误信息。
参考如下,如果不想处理异常可以使用_下划线替代错误返回值

	_, err := ioutil.ReadFile(".")
	if err!=nil {
		fmt.Println("有异常发生")
		return
	}

(3)自定义 error

我们可以根据需要创建自定义的error
【方式一:使用errors 包提供的New函数创建一个错误】
在这里插入图片描述
使用字符串创建一个错误,请类比fmt包的Errorf方法,差不多可以认为是New(fmt.Sprintf(…))。

	err:=errors.New("发生了一个异常")
	if err!=nil {
		fmt.Print(err)
	}  //输出 发生了一个异常

【方式二:fmt.Errorf】
当我们需要传入格式化的错误描述信息时,使用fmt.Errorf是个更好的选择。

fmt.Errorf("查询数据库失败,err:%v", err)

但是上面的方式会丢失原有的错误类型,只拿到错误描述的文本信息。

为了不丢失函数调用的错误链,使用fmt.Errorf时搭配使用特殊的格式化动词%w,可以实现基于已有的错误再包装得到一个新的错误。

fmt.Errorf("查询数据库失败,err:%w", err)

对于这种二次包装的错误,errors包中提供了以下三个方法。

func Unwrap(err error) error                 // 获得err包含下一层错误
func Is(err, target error) bool              // 判断err是否包含target
func As(err error, target interface{}) bool  // 判断err是否为target类型

(4)实现error接口

我们还可以自己定义结构体类型,实现`error接口

type MyError struct {
	msg string
}

func (myError *MyError) Error() string{
	return fmt.Sprintf("发生异常 %s", myError.msg)
}

(5)map获取

如果导致失败的原因只有一个,额外的返回值可以是一个布尔 值,通常被命名为ok。比如,当从一个 map 查询一个结果时,可以通过额外的布尔值判断是否成功:

func main() {
  var m=make(map[string]string)
  if v, ok := m["key"]; ok {
		fmt.Println("获取结果值:",v)
  }
}

(二)Recover和Panic

(1)Recover

在Go语言中,错误和异常是不相同的俩个概念。错误被认为是一种可以预期的结果;而异常则是一种非预期的结果, 发生异常可能表示程序中存在BUG或发生了其它不可控的问题。Go语言推荐使 用 recover 函数将内部异常转为错误处理,这使得用户可以真正的关心业务相关的错误处理。

Recover 是一个Go语言的内建函数,可以让进入宕机流程中的 goroutine 恢复过来,recover 仅在延迟函数 defer 中有效,在正常的执行过程中,调用 recover 会返回 nil 并且没有其他任何效果,如果当前的 goroutine 陷入恐慌,调用 recover 可以捕获到 panic 的输入值,并且恢复正常的执行。

可以在代码中使用 recover 来会恢复程序中意想不到的 panic,而 panic 只会触发当前 goroutine 中的 defer 操作。

通常来说,不应该对进入 panic 宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。

Go语言没有异常系统,其使用 panic 触发宕机类似于其他语言的抛出异常,recover 的宕机恢复机制就对应其他语言中的 try/catch 机制。

Recover()用法是:将Recover()写在defer中,并且在可能发生panic的地方之前,先调用此defer的东西(让系统方法域结束时,有代码要执行。)当程序遇到panic的时候(当然,也可以正常的调用出现的异常情况),系统将跳过后面的代码,进入defer,如果defer函数中recover(),则返回捕获到的panic的值。

【简单示例】

func main() {

	fmt.Println(divide(3,0))
}

func divide(num1 ,num2 int )int {
	defer func() {
		if err := recover(); err !=nil {
			fmt.Printf("error: %s\n", err)
		}
	}()
	return num1/num2
}

一个未受控的异常可以看作是程序的BUG。但是对于那些提供类似Web服务的框架而言;它们经常需要接入第三方的中间件。因为第三方的中间件是否存在BUG是否会抛出异常,Web框架本身是不能确定的。为了提高系统的稳定性,Web框架一般会通过 recover 来防御性 地捕获所有处理流程中可能产生的异常,然后将异常转为普通的错误返回。

【提示】在处理错误返回值的时候,没有错误的返回值最好直接写为 nil 。

(2)Panic

Panic是一种我们用来处理错误情况的机制。紧急情况可用于中止函数执行。当一个函数调用panic时,它的执行停止,并且控制流程到相关的延迟函数。

这个函数的调用者也会被终止,调用者的延迟函数也会被执行(如果有的话)。这个过程一直持续到程序结束。现在报告错误情况。

这种终止序列称为panic,可以由内置函数recover控制。

(3)剖析异常

panic 支持抛出任意类型的异常(而不仅仅是 error 类型的错误), recover 函数调用的返回值和 panic 函数的输入参数类型一致,它们的函数签名如下:

func panic(interface{}) 
func recover() interface{}

Go语言函数调用的正常流程是函数执行返回语句返回结果,在这个流程中是没有异 常的,因此在这个流程中执行 recover 异常捕获函数始终是返回 nil 。另一种 是异常流程: 当函数调用 panic 抛出异常,函数将停止执行后续的普通语句,但是之前注册的 defer 函数调用仍然保证会被正常执行,然后再返回到调用者。对于当前函数的调用者,因为处理异常状态还没有被捕获,和直接调用 panic 函数的行为类似。在异常发生时,如果在 defer 中执行 recover 调用,它可以捕获触发 panic 时的参数,并且恢复到正常的执行流程。

【提示】在非 defer 语句中执行 recover 调用是没有用的,不能捕获任何异常。recover 函数调用有着更严格的要求:我们必须在 defer 函数中直接调用 recover 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZWZhangYu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值