您现在的位置是:亿华云 > 域名
用Go语言异常机制模拟TryCatch异常捕捉!
亿华云2025-10-06 03:55:03【域名】9人已围观
简介有的同学看到Go和TryCatch一起出现,心里可能会说,难道Go语言升级了,加入了try...catch语句。哈哈,其实Go语言从创建之初就没打算加入try...catch语句,因为创建Go的那帮大
有的用Go语言异h异同学看到Go和TryCatch一起出现,心里可能会说,常机常捕难道Go语言升级了,制模捉加入了try...catch语句。用Go语言异h异哈哈,常机常捕其实Go语言从创建之初就没打算加入try...catch语句,制模捉因为创建Go的用Go语言异h异那帮大爷认为try...catch挺烦人的,如果滥用,常机常捕会造成程序混乱,制模捉所以就不打算加入try...catch(以后加不加入不好说)。用Go语言异h异
既然Go语言中并没有try...catch语句,常机常捕那么为何文章标题说要使用TryCatch呢?制模捉其实Go语言中只是没有try...catch语句,并不是用Go语言异h异没有异常处理机制。Go语言中的常机常捕异常处理机制就是著名的异常三剑客:panic、defer和recover。制模捉通过这3个家伙,是完全可以模拟出try...catch语句效果的,对了,后面还应该有个finally。在正式模拟try...catch语句之前,先来回顾下Go语言中的异常处理机制是如何玩的。服务器租用
Go语言中的异常处理机制
在前面提到,Go语言通过panic、defer和recover来处理异常的,那么这3个东西是什么呢?
不管是什么异常处理机制,核心的原理都是一样的,通常来讲,一个完善的异常处理机制需要由下面3部分组成。
抛出异常
处理异常的代码段
获取异常信息
下面先用Java的异常处理机制来说明这一点。
import java.io.IOException;
public class Main {
public static void main(String[] args) {
try
{
boolean ioException = false;
if (ioException) {
throw new IOException("ioexception");
} else {
throw new Exception("exception");
}
}
catch (IOException e) {
System.err.println(e);
}
catch (Exception e) {
System.out.println(e);
}
finally
{
System.out.println("finally");
}
}
}
上面的代码是标准的Java异常处理机制,try部分的throw用于抛出异常,而catch部分的代码段用于处理特定的异常,通过catch子句的参数e可以获取异常信息。所以对于Java来说,上述的3个异常重要的组成部分都有。云南idc服务商
对于Go语言来说,panic、defer和recover也分别对应了这3部分。其中panic是一个函数,用于抛出异常,相当于Java中的throw函数。defer是一个关键字,用于修饰函数,用defer修饰的函数,在抛出异常时会自动调用。recover是一个函数,用于获取异常信息,通常在用defer修饰的函数中使用。
下面是一段用Go语言处理异常的代码。
package main
import "fmt"
func main(){
// 处理异常的函数
defer func(){
fmt.Println("开始处理异常")
// 获取异常信息
if err:=recover();err!=nil{
// 输出异常信息
fmt.Println("error:",err)
}
fmt.Println("结束异常处理")
}()
exceptionFun()
}
func exceptionFun(){
fmt.Println("exceptionFun开始执行")
panic("异常信息")
fmt.Println("exceptionFun执行结束")
}
实现Go版的TryCatch
现在已经了解了Go语言的异常处理机制,那么接下来使用异常处理机制来模拟try...catch...finally语句。
现在来分析一下如果模拟。模拟的过程需要完成下面的工作。
try、catch和finally这3部分都有各自的站群服务器代码段,所以为了模拟try...catch...finally,需要用3个Go函数来分别模拟try、catch和finally部分的代码段。这3个Go函数是Try、Catch和Finally。
要确定这3个函数在什么地方调用。Try是正常执行的代码,所以在要首先调用Try函数。而Catch函数只有在抛出异常时调用,所以应该在用defer修饰的函数中调用,而且需要在Catch函数中获取异常信息,所以应该在使用cover函数获取异常信息后再调用Catch函数,通常会将异常信息直接作为参数传递给Catch函数。不管是否抛出异常,Finally函数都必须调用,所以应该用defer修饰Finally函数,而且是第1个用defer修饰的函数。这样,在当前函数结束之前一定刚回调用Finally函数。
触发异常,这就非常简单了,直接用panic函数即可。
上面清楚地描述了用Go语言的异常处理机制模拟try...catch...finally语句的基本原理,下面给出完整的实现代码。
package main
import (
"fmt"
)
type ExceptionStruct struct {
Try func()
Catch func(Exception)
Finally func()
}
type Exception interface{ }
func Throw(up Exception) {
panic(up)
}
func (this ExceptionStruct) Do() {
if this.Finally != nil {
defer this.Finally()
}
if this.Catch != nil {
defer func() {
if e := recover(); e != nil {
this.Catch(e)
}
}()
}
this.Try()
}
func main() {
fmt.Println("开始执行...")
ExceptionStruct{
Try: func() {
fmt.Println("try...")
Throw("发生了错误")
},
Catch: func(e Exception) {
fmt.Printf("exception %v\n", e)
},
Finally: func() {
fmt.Println("Finally...")
},
}.Do()
fmt.Println("结束运行")
}
上面的代码将Try、Catch、Finally函数都封装在了ExceptionStruct结构体中。然后调用方式就与前面的描述的一致了。执行这段代码,会输出如下图的信息。
image.png
增强版的TryCatch
到现在为止,其实已经完整地实现了try...catch...finally语句,但细心的同学会发现,这个实现有一点小问题。通常的try...catch...finally语句,try部分有且只有1个,finally部分是可选的,但最多只能有1个,而catch部分也是可选的,可以有0到n个,也就是catch部分可以有任意多个。但前面的实现,Catch函数只能指定一个,如果要指定任意多个应该如何做呢?其实很简单,用一个Catch函数集合保存所有指定的Catch函数即可。不过需要快速定位某一个Catch函数。在Java中,是通过异常类型(如IOException、Exception等)定位特定的catch子句的,我们也可以模拟这一过程,通过特定的异常来定位与该异常对应的Catch函数,为了方便,可以用int类型的异常代码。那么在调用Catch函数之前,就需要通过异常代码先定位到某一个Catch函数,然后再调用。下面就是完整的实现代码。
package main
import (
"log"
)
type Exception struct {
Id int // exception id
Msg string // exception msg
}
type TryStruct struct {
catches map[int]ExceptionHandler
try func()
}
func Try(tryHandler func()) *TryStruct {
tryStruct := TryStruct{
catches: make(map[int]ExceptionHandler),
try: tryHandler,
}
return &tryStruct
}
type ExceptionHandler func(Exception)
func (this *TryStruct) Catch(exceptionId int, catch func(Exception)) *TryStruct {
this.catches[exceptionId] = catch
return this
}
func (this *TryStruct) Finally(finally func()) {
defer func() {
if e := recover(); nil != e {
exception := e.(Exception)
if catch, ok := this.catches[exception.Id]; ok {
catch(exception)
}
finally()
}
}()
this.try()
}
func Throw(id int, msg string) Exception {
panic(Exception{ id,msg})
}
func main() {
exception.Try(func() {
log.Println("try...")
// 指定了异常代码为2,错误信息为error2
exception.Throw(2,"error2")
}).Catch(1, func(e exception.Exception) {
log.Println(e.Id,e.Msg)
}).Catch(2, func(e exception.Exception) {
log.Println(e.Id,e.Msg)
}).Finally(func() {
log.Println("finally")
})
}
执行结果如下图所示。
image.png
这个实现与Java中的try...catch...finally的唯一区别就是必须要调用Finally函数,因为处理异常的代码都在Finally函数中。不过这并不影响使用,如果finally部分没什么需要处理的,那么就设置一个空函数即可。
为了方便大家,我已经将该实现封装成了函数库,调用代码如下:
package main
import (
"exception"
"log"
)
func main() {
exception.Try(func() {
log.Println("try...")
exception.Throw(2,"error2")
}).Catch(1, func(e exception.Exception) {
log.Println(e.Id,e.Msg)
}).Catch(2, func(e exception.Exception) {
log.Println(e.Id,e.Msg)
}).Finally(func() {
log.Println("finally")
})
}
很赞哦!(4)
相关文章
- (4) 使用何种形式的域名后缀对网页搜索影响不大,但域名后缀也需要考虑方便用户记忆
- 从一道Promise执行顺序的题目看Promise实现
- 前端利器,6款开源的Web性能优化辅助工具推荐
- “Docker“生死”记,这条船还能开出去多远?
- 域后缀首选.com,.net,然后是.cn。后缀选择不当,导致流量损失。域名是企业与互联网网址之间的链接,关键是企业在网络上存在的标志。因此,选择好域名是开展网上工作的首要重要条件。
- 微软Build 2018展示Visual Studio功能:跨系统云编程
- 程序员:我只想安静地写代码,领导却跟我谈大局、讲奉献
- 合格的配置中心应有的素养
- 域名不仅仅是一个简单的网站。对于有长远眼光的公司来说,在运营网站之前确定一个优秀的域名对有长远眼光的公司来说是非常重要的。这对今后的市场营销、产品营销和企业品牌建设都具有十分重要的意义。优秀的域名是企业在市场竞争中获得持久优势的利器。
- “Docker“生死”记,这条船还能开出去多远?