GO-如何从异常中恢复(异常的抛出和捕获)

本文介绍了Go语言中如何处理异常。首先,详细阐述了如何通过`panic`抛出异常,它接受任意类型的参数。其次,讲解了使用`defer`和`recover`组合来捕获并处理异常,强调了`defer recover()`的正确使用方式。最后,讨论了在捕获异常后,如何通过类型转换调用异常对象的方法。

GO-如何从异常中恢复(异常的抛出和捕获)

1.抛出异常

当程序出现错误时,可以通过抛出异常的方式,中断程序的执行,并指明原因。
go通过panic将异常抛出

package main

import (
	"errors"
)

func main() {
	err := errors.New("this is an error")
	panic(err)
}

打印结果:

panic: this is an error

goroutine 1 [running]:
main.main()
	D:/Code/HelloGo/src/error/myRecover.go:63 +0x65

panic的参数是空接口—interface{},所以panic能够接收任何类型的参数。
上述例子时error类型,下面为string类型

package main

func main() {
	panic("error")
}

//打印结果
//panic: error

2.捕获异常

go中通过recover搭配defer的方式对异常进行捕获。

package main

import "fmt"

func catchError() {
	//recover()捕获异常
	errorInfo := recover()
	fmt.Println(errorInfo)
}

func main() {
    //确保异常抛出后,任能执行catchError()
	defer catchError()
	panic("throw error")
}

运行结果

throw error

当panic抛出异常后,执行catchError()方法。
catError()方法中,采用recover()方法,对异常进行捕获,并获取其信息,最总打印输出。

注意

  • 采用 defer recover() 的方式,异常捕获会失效。必须将recover包裹在一个方法中。
  • catchError()中没有recover()方法的化,执行完catchError()方法后,异常依旧会抛出。

3 调用抛出异常类型的方法

当我们想调用抛出对象上方法时,需要采用类型转换。将空接口转换成具体对象。

package main

import (
	"errors"
	"fmt"
)

func catchError() {
	//捕获异常
	errorInfo := recover()
	//将空接口转换成error类型
	err,ok := errorInfo.(error)
	if ok {
		//转换成功,调用error类型的Error()方法
		fmt.Println(err.Error())
	}else{
		//转换失败,将异常再次抛出,等待外部处理
		panic(errorInfo)
	}
}

func main() {
	defer catchError()
	err := errors.New("throw error")
	//抛出error类型的异常
	panic(err)
}

打印结果

throw error
### Go语言中 `defer` 捕获异常的用法 在Go语言中,`defer` 结合 `recover()` 是一种常见的捕获处理运行时异常(即 `panic`)的方法。通过这种方式可以有效地将不可控的 `panic` 转换为可控的错误返回值。 #### 基本原理 当发生 `panic` 时,程序会立即停止当前函数的执行,并依次调用之前注册的所有 `defer` 函数。如果在某个 `defer` 中调用了 `recover()` 并成功恢复,则程序不会继续传播该 `panic`,而是按照正常的控制流继续执行后续代码[^2]。 以下是具体实现的一个典型例子: ```go package main import ( "fmt" ) func mayPanic() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered from panic:", r) } }() panic("An unexpected error occurred!") } func main() { mayPanic() fmt.Println("Continuing execution after recovery.") } ``` 在这个示例中,`mayPanic` 函数内部发生了 `panic`,但由于存在一个带有 `recover()` 的 `defer`,因此能够捕捉到这次 `panic` 并打印相应的消息。最终,程序得以继续正常运行而不是崩溃退出[^3]。 #### 将 `panic` 转换为 `error` 返回 除了简单地恢复程序外,还可以进一步增强功能——把特定类型的 `panic` 转变为标准库支持的标准错误形式 (`error`) 来传递给调用方。下面展示了一个这样的应用场景实例: ```go package main import ( "bytes" "errors" "io" ) // 自定义错误类型模拟某种特殊条件下的 Panic 场景 var ErrTooLarge = errors.New("buffer too large") func readData(reader io.Reader, size int) ([]byte, error) { buf := make([]byte, size) defer func() { if p := recover(); p != nil { switch v := p.(type) { case string: err := fmt.Errorf(v) panic(err) // 继续向上层抛出已封装好的 Error 类型对象而非原始字符串 case error: panic(v) // 已经是合法的 Error 实现无需额外包装直接再抛即可 default: panic(p) // 非预期的情况维持原样重新引发 Panic } } }() n, err := reader.Read(buf[:]) if n > len(buf)/2 { // 故意制造溢出状况以便测试我们的机制有效性 panic(ErrTooLarge) } else if err != nil && err != io.EOF{ return nil,err } return buf[:n],nil } func safeRead(reader io.Reader, maxSize int)(data []byte,err error){ data,err=readData(reader,maxSize) if e:=recover();e!=nil{ // 外部再次包裹一层 Defer Recover 构造确保任何未被捕获异常都能被妥善处置 if er,ok:=e.(error);ok{ err=er }else{ panic(e)// 不属于预设范围内的异常则保持原有行为不做修改 } } return data,err } func main(){ var sampleReader=bytes.NewReader([]byte{'G','o'}) content,err:=safeRead(sampleReader,8) if err==ErrTooLarge{ fmt.Printf("Error:%v\n",err) }else{ fmt.Printf("Content Read Successfully:%s\n",string(content)) } } ``` 此代码片段展示了如何在一个更复杂的上下文中应用 `defer` `recover()` 技术来管理潜在危险的操作,同时提供清晰明了的结果反馈路径给使用者[^4]。 ### 注意事项 虽然这种方法非常有用,但也需要注意过度依赖可能会掩盖真正的逻辑缺陷或者隐藏难以调试的问题所在位置;另外频繁使用也可能影响性能表现因为每次进入新的作用域都需要创建额外的闭包开销。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值