前言例如,在介绍函数章节中,Go没有语言try...catch这种异常处理机制不能像java执行抛异常操作, 但是在Go可用于语言defer...recover...panic实现类似的机制try...catch效果;今天我们将讨论这个话题;
Go为什么不设计?try...catch异常机制
java里使用try/catch 机制,声明函数定义Expception使用层次不够简单,调用时异常处理过度泛滥,利用内存栈空间从底层向更高层次抛掷异常资源过度消耗。Go未使用设计机制java里try...catch但也实现了类似的异常机制 “捕捉” 将异常放入机制中,但更轻,只作为最终手段(处理错误)。
GO预定义了一个error接口类型;用错误值表示错误状态, 与其他数据类型一样,错误类型也是一种数据类型,可作为参数或返回值
type error interface{ Error() string }
错误定义
在GO语言里,没有类似Java如此强烈的面向对象特征, 可通过结构实现Error定义错误对象类型的方法,例如
type NullException struct{}?func (ne *NullException) Error() string{ return "NullPointException"}?func Trim(s *string) (string,error){ if s == nil{ return "",&NullException{}}else{ return strings.Trimspace(*s),nil }}
例如,上述代码定义了异常对象的类型NullException; 可以通过 err:=&NullException{}生成错误对象;
上面是通过type struct定义错误; 除此之外,还可以使用errors内置函数包errors.New产生新的错误类型对象;如下代码:
func Trim(s *string) (string,errors.New("NullPointException") }else{ return strings.Trimspace(*s),nil }}
与上述方法相比,代码更简单,很多源代码都是这样写的; 让我们来看看一个完整的例子
func TestException(t *testing.T){ var s *string? if rtn,err := Trim(s); err == nil{ fmt.Printf("Trim(%v)=%v
",s,rtn) }else{ fmt.Printf("Trim(%v) throw exception: %v
",err) }? var a string if rtn,err := Trim(&a); err == nil{ fmt.Printf("Trim(%v)=%v
",&a,err) }}??===== OUTPUT ======== RUN TestExceptionTrim(<nil>) throw exception: NullPointException Trim(0xc000273a10)= --- PASS: TestException (0.00s)PASS
在上述代码中,第一次调用Trim(s),s定义为string对于指针类型的变量,定义后自动初始化为指针类型nil,所以Trim(s)调用会返回NullExpcetion对象; 也就打印出Trim(<nil>) throw exception: NullPointException ;
对于第二个Trim(&a);a定义为string类型对象,string定义基本类型对象后,自动初始化为空字符串("";),所以Trim(&a)调用,传入string指针不为空nil(而是空字符串"";变量指针),此时返回的错误对象是nil,没有错;返回是空字串Trim以后的结果,或者空字符串;打印出来Trim(0xc000273a10)=
错误的判断
有时在函数处理过程中,可能会出现不同类型的错误条件;例如,对于文件处理场景;有时可能是文件路径错误,有时可能是文件的状态错误;当发生各种错误时,对错误的判断具有使用场景的价值。使用类型断言或类型判断(type-switch)这是一种非常有效的方法来处理这个场景,并且可以 根据错误场景进行补救和恢复。使用类型断言或类型判断(type-switch)这是一种非常有效的方法来处理这个场景,并且可以 根据错误场景进行补救和恢复。
看下面的代码
type NotFoundException struct{}?func (ne NotFoundException) Error() string{ return "NotFoundException"}?type NotAllowException struct{}?func (ne NotAllowException) Error() string{ return "NotAllowException"}?func ReadFile(path string) ([]byte,error){ n := len(path) if n <= 1{ return nil,NotFoundException{}}else if n <= 12{ return nil,NotAllowException{}}? return[]byte(path),nil}?func ReadOne(path string){ if rtn,err := ReadFile(path); err == nil{ fmt.Printf("ReadFile(%v)=%v
",path,rtn) }else{ switch err.(type){ case NotFoundException: fmt.Printf("ReadFile(%v) throw NotFoundException: %v
",err) case NotAllowException: fmt.Printf("ReadFile(%v) throw NotAllowException: %v
",err) default: fmt.Printf("ReadFile(%v) throw UnkNowException: %v
",err) }}}
上面的代码ReadOne在函数中,有错误判断的处理方法; 在ReadOne函数里调用ReadFile函数,在ReadFile如果函数简单地实现,path长度不大于1,返回错误NotFoundException;如果path长度不大于12;返回错误;NotAllowException;其他正常返回;
ReadFile函数错误类型可以返回多种类型;ReadOne函数是通过的type-swtich的方式,对err判断对象的类型; 如果是NotFoundException执行逻辑;如果是,NotAllowException如果两种错误类型不匹配,则执行第三段逻辑; 错误类型NotFoundException和NotAllowException都在ReadOne定义函数上方的实际调用代码:
func TestException2(t *testing.T){ var s string? s = "/" ReadOne(s)? s = "/" ReadOne(s)? s = "/readme.txt" ReadOne(s)? s = "/home/readme.txt" ReadOne(s)}?===== OUTPUT ======== RUN TestException2ReadFile(/) throw NotFoundException: NotFoundException ReadFile(/readme.txt) throw NotAllowException: NotAllowException ReadFile(/home/readme.txt)=[47 104 111 109 101 47 114 101 97 100 109 101 46 116 120 116]--- PASS: TestException2 (0.00s)PASS
结束语
错误处理是每种编程语言中需要面对的问题,也是编程过程中必须考虑的问题;如果处理错误,代码的稳定性会很好。今天的文章通过代码实例向您介绍GO语言中有一些关于错误处理的知识;我们用代码练习,写一个更稳定的程序。今天的文章通过代码实例向您介绍GO语言中有一些关于错误处理的知识;我们用代码练习,写一个更稳定的程序。
欢迎继续关注 GO语言编程训练
?