有的同学看到Go和TryCatch一起出现,心里可能会说,难道Go语言升级了,加入了try...catch语句。哈哈,其实Go语言从创建之初就没打算加入try...catch语句,因为创建Go的那帮大爷认为try...catch挺烦人的,如果滥用,会造成程序混乱,所以就不打算加入try...catch(以后加不加入不好说)。

既然Go语言中并没有try...catch语句,那么为何文章标题说要使用TryCatch呢?其实Go语言中只是没有try...catch语句,并不是没有异常处理机制。Go语言中的异常处理机制就是著名的异常三剑客:panic、defer和recover。通过这3个家伙,是完全可以模拟出try...catch语句效果的,对了,后面还应该有个finally。在正式模拟try...catch语句之前,先来回顾下Go语言中的异常处理机制是如何玩的。

Go语言中的异常处理机制

在前面提到,Go语言通过panic、defer和recover来处理异常的,那么这3个东西是什么呢?

不管是什么异常处理机制,核心的原理都是一样的,通常来讲,一个完善的异常处理机制需要由下面3部分组成。

  • 抛出异常
  • 处理异常的代码段
  • 获取异常信息

下面先用Java的异常处理机制来说明这一点。

  1. import java.io.IOException;
  2.  
  3. public class Main {
  4.  
  5. public static void main(String[] args) {
  6. try
  7. {
  8. boolean ioException = false;
  9. if (ioException) {
  10. throw new IOException("ioexception");
  11. } else {
  12. throw new Exception("exception");
  13. }
  14. }
  15. catch (IOException e) {
  16. System.err.println(e);
  17. }
  18. catch (Exception e) {
  19. System.out.println(e);
  20. }
  21. finally
  22. {
  23. System.out.println("finally");
  24. }
  25. }
  26. }

  

上面的代码是标准的Java异常处理机制,try部分的throw用于抛出异常,而catch部分的代码段用于处理特定的异常,通过catch子句的参数e可以获取异常信息。所以对于Java来说,上述的3个异常重要的组成部分都有。

对于Go语言来说,panic、defer和recover也分别对应了这3部分。其中panic是一个函数,用于抛出异常,相当于Java中的throw函数。defer是一个关键字,用于修饰函数,用defer修饰的函数,在抛出异常时会自动调用。recover是一个函数,用于获取异常信息,通常在用defer修饰的函数中使用。

下面是一段用Go语言处理异常的代码。

  1. package main
  2.  
  3. import "fmt"
  4.  
  5. func main(){
  6. // 处理异常的函数
  7. defer func(){
  8. fmt.Println("开始处理异常")
  9. // 获取异常信息
  10. if err:=recover();err!=nil{
  11. // 输出异常信息
  12. fmt.Println("error:",err)
  13. }
  14. fmt.Println("结束异常处理")
  15. }()
  16. exceptionFun()
  17. }
  18.  
  19. func exceptionFun(){
  20. fmt.Println("exceptionFun开始执行")
  21. panic("异常信息")
  22. fmt.Println("exceptionFun执行结束")
  23. }

  

实现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语句的基本原理,下面给出完整的实现代码。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type ExceptionStruct struct {
  6. Try func()
  7. Catch func(Exception)
  8. Finally func()
  9. }
  10. type Exception interface{}
  11. func Throw(up Exception) {
  12. panic(up)
  13. }
  14. func (this ExceptionStruct) Do() {
  15. if this.Finally != nil {
  16.  
  17. defer this.Finally()
  18. }
  19. if this.Catch != nil {
  20. defer func() {
  21. if e := recover(); e != nil {
  22. this.Catch(e)
  23. }
  24. }()
  25. }
  26. this.Try()
  27. }
  28.  
  29. func main() {
  30. fmt.Println("开始执行...")
  31. ExceptionStruct{
  32. Try: func() {
  33. fmt.Println("try...")
  34. Throw("发生了错误")
  35. },
  36. Catch: func(e Exception) {
  37. fmt.Printf("exception %v\n", e)
  38. },
  39. Finally: func() {
  40. fmt.Println("Finally...")
  41. },
  42. }.Do()
  43. fmt.Println("结束运行")
  44. }

  

上面的代码将Try、Catch、Finally函数都封装在了ExceptionStruct结构体中。然后调用方式就与前面的描述的一致了。执行这段代码,会输出如下图的信息。

增强版的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函数,然后再调用。下面就是完整的实现代码。

  1. package main
  2.  
  3. import (
  4. "log"
  5. )
  6.  
  7. type Exception struct {
  8. Id int // exception id
  9. Msg string // exception msg
  10. }
  11.  
  12. type TryStruct struct {
  13. catches map[int]ExceptionHandler
  14. try func()
  15. }
  16.  
  17. func Try(tryHandler func()) *TryStruct {
  18. tryStruct := TryStruct{
  19. catches: make(map[int]ExceptionHandler),
  20. try: tryHandler,
  21. }
  22. return &tryStruct
  23. }
  24.  
  25. type ExceptionHandler func(Exception)
  26.  
  27. func (this *TryStruct) Catch(exceptionId int, catch func(Exception)) *TryStruct {
  28. this.catches[exceptionId] = catch
  29. return this
  30. }
  31.  
  32. func (this *TryStruct) Finally(finally func()) {
  33. defer func() {
  34. if e := recover(); nil != e {
  35.  
  36. exception := e.(Exception)
  37.  
  38. if catch, ok := this.catches[exception.Id]; ok {
  39. catch(exception)
  40. }
  41.  
  42. finally()
  43. }
  44. }()
  45.  
  46. this.try()
  47. }
  48.  
  49. func Throw(id int, msg string) Exception {
  50. panic(Exception{id,msg})
  51. }
  52.  
  53. func main() {
  54.  
  55. exception.Try(func() {
  56. log.Println("try...")
  57. // 指定了异常代码为2,错误信息为error2
  58. exception.Throw(2,"error2")
  59. }).Catch(1, func(e exception.Exception) {
  60. log.Println(e.Id,e.Msg)
  61. }).Catch(2, func(e exception.Exception) {
  62. log.Println(e.Id,e.Msg)
  63. }).Finally(func() {
  64. log.Println("finally")
  65. })
  66. }

  执行结果如下图所示。

这个实现与Java中的try...catch...finally的唯一区别就是必须要调用Finally函数,因为处理异常的代码都在Finally函数中。不过这并不影响使用,如果finally部分没什么需要处理的,那么就设置一个空函数即可。

为了方便大家,我已经将该实现封装成了函数库,调用代码如下:

  1. package main
  2. import (
  3. "exception"
  4. "log"
  5. )
  6.  
  7. func main() {
  8.  
  9. exception.Try(func() {
  10. log.Println("try...")
  11. exception.Throw(2,"error2")
  12. }).Catch(1, func(e exception.Exception) {
  13. log.Println(e.Id,e.Msg)
  14. }).Catch(2, func(e exception.Exception) {
  15. log.Println(e.Id,e.Msg)
  16. }).Finally(func() {
  17. log.Println("finally")
  18. })
  19. }

  

获得本文源代码,请关注”极客起源“或”欧瑞科技“公众号,并输入308178获得源代码。

 

用Go语言异常机制模拟TryCatch异常捕捉的更多相关文章

  1. 用Go语言异常机制模拟TryCatch异常捕捉1

    有的同学看到Go和TryCatch一起出现,心里可能会说,难道Go语言升级了,加入了try...catch语句.哈哈,其实Go语言从创建之初就没打算加入try...catch语句,因为创建Go的那帮大 ...

  2. C++ 异常机制分析

    C++异常机制概述 异常处理是C++的一项语言机制,用于在程序中处理异常事件.异常事件在C++中表示为异常对象.异常事件发生时,程序使用throw关键字抛出异常表达式,抛出点称为异常出现点,由操作系统 ...

  3. java中的异常机制(编译时异常)

    / * 1 异常机制的原理 * 异常是什么:就是错误的另外一种说法; * 在java中,有一个专门模拟所有异常的类,所有的异常都必须继承这个类:Throwable; * 本质是:当程序出错以后,jvm ...

  4. C++ 异常机制分析(C++标准库定义了12种异常,很多大公司的C++编码规范也是明确禁止使用异常的,如google、Qt)

    阅读目录 C++异常机制概述 throw 关键字 异常对象 catch 关键字 栈展开.RAII 异常机制与构造函数 异常机制与析构函数 noexcept修饰符与noexcept操作符 异常处理的性能 ...

  5. 【转】C++ 异常机制分析

    阅读目录 C++异常机制概述 throw 关键字 异常对象 catch 关键字 栈展开.RAII 异常机制与构造函数 异常机制与析构函数 noexcept修饰符与noexcept操作符 异常处理的性能 ...

  6. 全面理解java异常机制

    在理想状态下,程序会按照我们预想的步骤一步一步的执行,但是即使你是大V,你也不可避免出错,所以java为我们提供了异常机制.本文将会从以下几个方面介绍java中的异常机制: 异常机制的层次结构 异常的 ...

  7. Java 异常机制

    Java 异常机制 什么是异常 异常指不期而至的各种状况,如:文件找不到.网络连接失败.非法参数等.异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程 为什么要有异常 什么出错了 哪里出错了 ...

  8. C++ 异常机制(上)

    目录 一.概念 二.异常的好处 三.基本语法 四.栈解旋 五.异常接口声明 六.异常对象的内存模型 七.异常对象的生命周期 一.概念 异常:存在于运行时的反常行为,这些行为超过了函数的正常的功能范围. ...

  9. Java异常机制

    Java异常分类 异常表明程序运行发生了意外,导致正常流程发生错误,例如数学上的除0,打开一个文件但此文件实际不存在,用户输入非法的参数等.在C语言中我们处理这类事件一般是将其与代码正常的流程放在一起 ...

随机推荐

  1. vue 路由组件不重新加载

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  2. activity fragment 转场动画

    http://www.cnblogs.com/avenwu/p/3372736.html v4 fragment fragmentTransaction.setCustomAnimations(R.a ...

  3. SDWebImage学习

    SDWebImage学习 SDWebImage版本是:'4.2.2' SDWebImage是iOS开发中常用的图片加载的库,能下载并缓存图片.这次就着重介绍SDWebImage的特色功能:下载与缓存. ...

  4. [学些东西]用爬虫练习网站来练习burp suite

    最近看爬虫的内容.刚好看到黑板客爬虫第二关http://www.heibanke.com/lesson/crawler_ex01. ADO的i春秋课程里面提到的.另外推荐学习爬虫的好书<web ...

  5. Java之运行时异常与编译时异常区别

    Java中用2种方法处理异常: 1.在发生异常的地方直接处理: 2.将异常抛给调用者,让调用者处理. Java异常可分为3种: (1)编译时异常:Java.lang.Exception (2)运行期异 ...

  6. JDBC编程步奏、问题总结(一)

    jdbc编程步骤: 1. 加载数据库驱动 2. 创建并获取数据库链接 3. 创建jdbc statement对象 4. 设置sql语句 5. 设置sql语句中的参数(使用preparedStateme ...

  7. ABAP读取工单状态 STATUS_READ

    *&---------------------------------------------------------------------* *& Report YDEMO_013 ...

  8. PAT 天梯赛 L2-008. 最长对称子串 【字符串】

    题目链接 https://www.patest.cn/contests/gplt/L2-008 思路 有两种思路 第一种 遍历每一个字符 然后对于每一个 字符 同时 往左 和 往右 遍历 只要 此时 ...

  9. UVA11752 The Super Powers —— 数论、枚举技巧

    题目链接:https://vjudge.net/problem/UVA-11752 题意: 一个超级数是能够至少能表示为两个数的幂,求1~2^64-1内的超级数. 题解: 1.可知对于 n = a^b ...

  10. HDU1133 Buy the Ticket —— 卡特兰数

    题目链接:https://vjudge.net/problem/HDU-1133 Buy the Ticket Time Limit: 2000/1000 MS (Java/Others)    Me ...