前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入Go的异常错误处理机制(二)理解

深入Go的异常错误处理机制(二)理解

作者头像
阿伟
发布2019-08-06 10:08:40
1.1K0
发布2019-08-06 10:08:40
举报
文章被收录于专栏:GoLang那点事
开篇词
  • 上一篇文章分享了Go的异常,错误处理使用,未读过的可以点击回顾一下,我们知道程序运行中,有异常,有错误,那么什么是异常,什么是错误,和其他语言相比,Go的异常错误机制有什么优点,缺点?我们如何更好的理解,如何用Go写出更健壮的程序,今天来聊一聊这些问题。
异常和错误
  • 关于异常和错误每个人都有自己的理解,很多人往往把这个混为一谈,认为他们是等价的,这里我们从Java 和Go两种语言异常错误体系的设计分析来试图回答这个问题。
  • Java中,Throwable是所有错误(Error)和异常 (Exception) 的基类,整的来说,它们都是程序运行过程中可能出现的问题,区别在哪里呢? Exception是可预料的,而Error是不可预料的,举个例子,工程队要盖一栋楼,我们把盖这栋楼的过程比作程序一段运行的过程,在盖楼的过程中,建筑人员对于对于瓷砖损坏,下雨,断电,设计错误等都是在意料之内的,并且在发生这些问题时是由恢复,补救措施的,而对于地震,地面塌陷,狂风这些问题是不可预知,意料之外的,并且这些问题真的发生了,也是无法补救,恢复的,只能重新来过。Exception我理解为在程序运行中正常情况下意料之中发生的事,是可以被程序员处理,补救,有机会回到正常处理流程的,而Error在程序运行中非正常成矿下发生后是无法被处理,恢复,比如内存溢出,栈溢出等。
  • Go的异常错误设计体系只有Error,任何一切都在方法返回值中返回可能发生的错误,那么go有没有运行过程中意料之外的错误呢,答案是有呢,panic和defer以及recover共同组成了这个体系,但这个体系最终还是被返回Error所处理,什么含义呢,就是在意料之外的panic发生时,在defer中通过recover捕获这个恐慌,转化为错误通过方法返回值告诉方法调用者,看到这里,其实从字面意思,Go中弱化了异常,一切皆错误,都被包装成类似Code,Message的形式返回方法调用者,直到有调用者去处理, 这也是Go的设计精髓,简化没必要存在的。
从编码上看看Go和Java的异常设计思想
  • 如果你看过Go的许多代码,那么 iferr!=ni, 应该随处可见,尤其是业务代码开发中,是的,Go没有类似 trycatch 这样的语句,Go对错误的价值观是可编程,我们看下面的代码:
代码语言:javascript
复制
result1,err := func1()
if err != nil{
    return nil,,err
}
result2,err := func2()
if err != nil{
    return nil,err
}
result3,err := func3()
if err != nil{
    return nil,err
}
return result1+result2+result3,nil
代码语言:javascript
复制
try{
    result1 = method1();
    reuslt2 = method2();
    result3 = method3();
    return result1+result2+result3;
}cache(Exception e){
    log.error(e);
    return null;
}
  • 上面的代码我想了解Java和Go的人都知道,我们从多个方面来看看上面的代码,第一:从顺序角度来看,是否Go更能被理解,每一个方法都有一个结果值和一个可能发生的错误值,而Java需要更多的语法,意味着需要更多理解,思考;第二:从对异常错误处理角度来看,Go中程序员对err有更多的操作空间,有更多的可编程性,而Java中相对可编程性弱化了许多;第三:也是最直观的,代码量我们发现Go的代码量比Java多了将近一半,而这种代码并没有什么技术量,重复的代码到处都是,是的,这也是许多开发者对Go不满意的地方,但这种是可以通过开发者代码设计去规避的,这里暂且不讨论

  • Java中通过 thrownew 抛出一个异常,通过 trycache捕获,而Go中通过 panic抛出一个恐慌,通过 defer和recover来处理,我们来看看代码,在分析
代码语言:javascript
复制
func test() (err error){
    defer func(){
        if e:=recover(); e != nil{
             err = e.(error)
        }
    }()    
    panic("发生恐慌了")
    return err
}
代码语言:javascript
复制
public Result test(){
    try{
        throw new RuntimeException("发生异常了")
    }cache(Exception e){
        //处理错误
        1 打印错误,忽略
        2 throw e,继续抛出
        //转化为code,message
        3 new Result(code,e.message) 
    }
}
  • 我们分析上面两段代码,当异常或者恐慌发生时,我们可以看到Go中在defer里对通过recover捕获panic,将其转化为一个错误,通过返回值的形式返回,而在Java中异常发生时,捕获以后处理方式为要么打印,要么throw出去抛给上层的方法调用者,站在方法全局来看,当你是一个调用者时,你期望的是什么?如果你有接口交互的开发经验,我想你不会给调用接口的人抛出一个exception 或者 panic,他会不高兴的,同样你也不希望接口返回的是一堆堆栈信息,那么 在上面Java的cache中最终是返回一个Result,包含code,message,这样去看,我们对Java异常包装后是否和Go的错误设计有异曲同工之妙,同样的Go通过panic和defer,recover也可以为try,cache, throw这样的处理,但语言层面的设计的本质是不一样的(记住哦),相互的转化只是人为的在包装而已,请不要偏离正轨哦。
聊聊exception和panic给Java和Go带来了什么
  • 不管是Go还是Java,我们知道当程序启动后,对操作系统而言都是一个进程,Java中,在一个进程中可以启动多个线程,线程是Java的最小单位,Go中,一个进程中依然会有多个线程,但这不是最小单位,协程是Go的最小单位;
  • Exception在Java中的作用域是当前线程,也就是说当Exception中发生时,只会影响到当前线程的执行,终止的是当前线程。
  • Panic在Go中的作用域是整个进程,当Panic发生时,如果当前协程没捕获,则整个Go的进程就会终止,这是非常可怕的。
  • 所以需要开发人员在go的错误处理时需要谨慎,需要手工处理所有的err,尤其在对panic可能发生的地方需要捕获,这稍微增加了开发人员的心智负担
  • 同样的,我们能看到Go的程序需要更多的严谨性,健壮性,所以在开发阶段,快速试错,让尽可能多的错误立刻出现,然后修复。
  • 提醒的是,尤其是在设计一些底层框架,方法时,一定需要对panic处理,转化为err返回,否则框架抛出的panic会导致整个Go的程序结束
总结
  • 在我看来,并没有绝对,Java中对异常和错误有一个比较清晰的边界,通过类继承体系进行隔离,错误并不在程序员的考虑范围之内,通过异常体系和控制流程来实现业务逻辑,往往也容易被滥用;而Go中并没有,且弱化了异常的概念,并提供了将异常转化为错误的方法。一切皆错误,拥有更好的可编程性,但同时也带来诸如 iferr!=nil的这样的代码到处都是,不同的编程语言对异常错误体系设计不一样,也代表不同开发者的思想,没有对与错,个人认为都能解决特定的问题,同时也会带来一定的困扰,一定要理解这种异常错误体系设计在当前编程语言中的设计思想,才能更好的使用,写出更优雅的代码。

Go认为:

  • 让程序员更直接的接触错误,从而处理
  • 错误是一种可编程的值
  • 强调的是,无论何时,检查错误都是至关重要的,而不是如何避免检查错误
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GoLang那点事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 开篇词
  • 异常和错误
  • 从编码上看看Go和Java的异常设计思想
  • 聊聊exception和panic给Java和Go带来了什么
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档