深入Go的错误处理机制使用

开篇词

程序运行过程中不可避免的发生各种错误,要想让自己的程序保持较高的健壮性,那么异常,错误处理是需要考虑周全的,每个编程语言提供了一套自己的异常错误处理机制,在Go中,你知道了吗?接下来我们一起看看Go的异常错误机制。

Go错误处理,函数多返回值是前提

  • 首先我们得明确一点,Go是支持多返回值的,如下,sum函数进行两个int型数据的求和处理,函数结果返回最终的和(z)以及入参(x,y),既然支持多返回值,同理,我们能否把错误信息返回呢?当然是可以的
func sum (x,y int) (int,int,int){
    z := x+y
    return z,x,y
}

Go内置的错误类型

  • 在Go中内置了一个error接口用来用来处理错误
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
//翻译下来就是:
//错误的内置接口类型是 error,没有错误用 nil 表示
type error interface {??
    Error() string
}
  • 我们来看Go内置的一个关于error接口的简单实现
func New(text string) error {????????
    return &errorString{text}
}
// errorString is a trivial implementation of error.
翻译
// 把error转换成String是错误的简单实现
type errorString struct {????????
    s string
}
func (e *errorString) Error() string {?
    return e.s
}

我们可以看到errorString结构体实现了 Error()string 接口,通过New()方法返回一个errorString指针类型的对象。

看到这里不知道大家想到没,Go对错误的处理就是显示的通过方法返回值告诉你需要对错误进行判断和处理。也就是错误对你是可见的,这也需要开发人员在方法中尽可能的考虑到各种发生的错误,并返回给方法调用者。

Go内置的异常捕获

我们知道程序在运行时会发生各种各样的运行时错误,比如数组下标越界异常,除数为0的异常等等,而这些异常如果不被处理会导致go程序的崩溃,那么如何捕获这些运行时异常转化为错误返回给上层调用链,就让我一起看看这几个关键字:panic, defer recover,此处我们不讨论原理。

  • go内置了这几个关键字,下面是这几个关键字的含义:

    1. panic 恐慌
    2. defer 推迟,延缓
    3. recover 恢复

我们把运行时发生异常称为发生了一个恐慌,我们也可以手动抛出一个恐慌,如下

func TestPanic(){
    panic("发生恐慌了")
}
//截取一部分结果,我们看到程序终止了,打印了堆栈信息
anic: 发生恐慌了 [recovered]
    panic: 发生恐慌了
goroutine 19 [running]:
testing.tRunner.func1(0xc0000b6100)
    D:/sdk/go12/src/testing/testing.go:830 +0x399
panic(0x521da0, 0x57bb10)
    D:/sdk/go12/src/runtime/panic.go:522 +0x1c3
gome_tools/basic.TestPanic(...)
    D:/gome_space/gome_tools/basic/array_slice.go:101

恐慌发生了怎么处理呢,这时需要defer和recover一起协作,defer什么意思呢,是表示这个方法最后执行的一段代码,无论这个方法发生错误,异常等,defer里面的里代码一定会被执行,而我们可以在defer中通过recover关键字恢复我们的恐慌,将之处理,转化为一个错误并打印,如下代码:


func TestDeferAndRecover(){
    defer func(){
        if err:=recover(); err != nil{
            fmt.Println("异常信息为:",err)
        }
    }()
    panic("发生恐慌了")
}
//结果
异常信息为: 发生恐慌了

Go异常错误处理示例

  • 接下来我们看一个除法函数,函数中,如果被除数为0,则除法运算是失败的
func division(x,y int) (int,error){
    //如果除数为0,则返回一个错误信息给上游
    if y == 0{
        return 0,errors.New("y is not zero")
    }
    z := x / y
    return z ,nil
}

result, err := division(1,0)
if err != nil {
    //处理错误逻辑
}
//处理正常逻辑

如上,division函数里面判断y等于0时,给调用者返回一个错误信息,调用者通过两个变量来接受division的返回值,判断 err是否为空做出不同的错误处理逻辑

  • 有些错误,我们知道是不可能发生的,那么如何忽略这类错误呢?

还是上面的 division(x,y)(z,error) 函数,假设我们入参传(4,2)进去,这时我们是清楚的知道不可能发生错误,我们可以按如下处理,通过下划线 _ 忽略这个返回值。

//通过_忽略第二个返回值
result, _ := division(1,0)
//打印结果
fmt.Println(result)
  • 只要是人,总有考虑不周的时候,总有想不到的时候,

还是division(x,y)(z,error)函数,假设小明忘记了或者没想到要判断除数为0的情况,写出来的代码如下:

func division(x,y int) (int,error){
    z := x / y
    return z ,nil
}

小红在调用上面的方法时写成了 result,_ := division(1,0),很明显division方法是会发生错误的,错误信息如下,integer divide by zero ,被除数为0,我们知道程序出错了,并且整个程序终止了

tips: Go语言中,一旦某一个协程发生了panic而没有被捕获,那么导致整个go程序都会终止,确实有点坑,但确实如此(了解java的人都知道,在java中一个线程发生发生了异常,只要其主线程不曾终止,那么整个程序还是运行的) ,但go不是这样的,文章最后我会写一个例子,大家可以看看。

通过上面的tips,我们知道,我们不能让我们的方法发生panic,在不确保方法不会发生panic时一定要捕获,谨记。

panic: runtime error: integer divide by zero [recovered]
    panic: runtime error: integer divide by zero
  • 发生panic时,如何捕获这个panic给上游返回一个error
func division(x,y int) (result int,err error){
    defer func(){
        if e := recover(); e != nil{
            err = e.(error)
        }
    }()
    result = x / y
    return result ,nil
}

这段代码什么意思呢?当我们division(1,0)时,一定会报除0异常,division函数声明了返回值result(int型),err(error型),当x / y发生异常时,在defer函数中,我们通过recover()函数来捕获发生的异常,如果不为空,将这个异常赋值给返回结果的变量 err,我们再来调用这个函数division(1,0)看看输出什么,如下,是不是将堆栈信息转化为了一段字符串描述。

0 runtime error: integer divide by zero
  • 如何自定义自己的错误类型

我们知道go中关于错误定义了一个接口,如果想要自定义自己的错误类型,我们只需要实现这个接口就可以了,还是这个函数,我们为其定义一个除数为0的错误


type DivideByZero struct{
    //错误信息
    e string
    //入参信息(除数和被除数)
    param string
}
//实现接口中的Error()string方法,组装错误信息为字符串
func (e *DivideByZero) Error() string {?
    buffer := bytes.Buffer{}
    buffer.WriteString("错误信息:")
    buffer.WriteString(divideByZero.e)
    buffer.WriteString(",入参信息:")
    buffer.WriteString(divideByZero.param)
    return buffer.String()
}

func division(x,y int) (int,error){
    //如果除数为0,则返回一个错误信息给上游
    if y == 0{
        //这个时候我们返回如下错误
        return 0, &DivideByZero{
            e:"除数不能0",
            param:strings.Join([]string{strconv.Itoa(x),strconv.Itoa(y)},","),
        }
    }
    z := x / y
    return z ,nil
}
//最终结果
0 错误信息:除数不能为0,入参信息:1,0

最后补一下上面说的示例

上文提到,go中一旦某一个协程发生了panic而没被recover,那么整个go程序会终止,而Java中,某一线程发生了异常,即便没被catche,那么只是这个线程终止了,Java程序是不会终止的,只有主线程完成Java程序才会结束,看下面两段代码


public static void main(String []args){
    new Thread(new Runnable() {???
        @Override???
        public void run() {
            throw new RuntimeException("抛出异常了");???
        }
    }).start();
    try {???
        Thread.sleep(10 * 1000);
    }catch (InterruptedException e) {
    }
}

func main(){
    go func() {
        panic("发生恐慌了")
    }()
    time.Sleep(10 * time.Second)
}

上面两端代码含义都是一样的,启动后各开一个线程和协程,在线程和协程内分别主动抛出异常,但结果不一样,java的主线程会休眠10秒钟后结束,而go主协程会立即结束。

欢迎大家关注微信公众号:“golang那点事”,更多精彩期待你的到来

原文地址:https://www.cnblogs.com/sy270321/p/11508797.html

时间: 2024-10-10 00:29:06

深入Go的错误处理机制使用的相关文章

Map/Reduce 工作机制分析 --- 错误处理机制

前言 对于Hadoop集群来说,节点损坏是非常常见的现象. 而Hadoop一个很大的特点就是某个节点的损坏,不会影响到整个分布式任务的运行. 下面就来分析Hadoop平台是如何做到的. 硬件故障 硬件故障可以分为两种 - JobTracker节点损坏和TaskTracker节点损坏. 1. JobTracker节点损坏 这是Hadoop集群中最为严重的错误. 出现了这种错误,那就只能重新选择JobTracker节点,而在选择期,所有的任务都必须停掉,而且当前已经完成了的任务也必须通通重来. 2.

php中的错误处理机制

1.如果没有错误处理机制会怎样? 案例1: <?php  $fp=fopen("aa.txt","r");//打开文件,没有验证,是否存在该文件?  echo "ok";   ?> 结果: Warning: fopen(aa.txt) [function.fopen]: failed to open stream: No such file or directory in E:\Software_default\wamp_wwwroo

linux系统编程之错误处理机制

在讲解liunx错误处理机制之前我们先来看一段代码: 1 #include<sys/types.h> 2 #include<sys/stat.h> 3 #include<fcntl.h> 4 #include<stdio.h> 5 #include<stdlib.h> 6 7 int main(void) 8 { 9 int fd; 10 fd=open("abc",O_WRONLY); 11 if(fd<0){ 12

关于面向切面编程的部分内容-错误处理机制

错误处理机制. 面对多个web服务器,多线程处理,我们想把错误信息记录到一个txt文档中. 但是把错误信息写到内存是很快.写到硬盘上就有一堆的问题.比如说读写慢.并发问题. 今天我们就利用这个实现错误处理 此文以MVC为例 1.首先要在  golable  文件的  protected void Application_Start() 注册一个错误处理机制. MVC中自带一个  过滤器 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filter

php中错误处理机制

php中,异常处理机制是有限的,无法自动抛出异常,必须手动进行,并且内置异常有限. php把许多异常看作错误,这样就可以把这些异常想错误一样用set_error_handler接管,进而主动抛出异常. 比如以下warning类型的错误是捕获不到的 :   Warning: Division by zero in 1 try{ 2 $a = 5/0; 3 }catch (Exception $e){ 4 echo '错误信息:',$e->getMessage(); 5 } 使用set_error_

hadoop 错误处理机制

hadoop 错误处理机制 1.硬件故障 硬件故障是指jobtracker故障或TaskTracker 故障 jobtracker是单点,若发生故障,目前hadoop 还无法处理,唯有选择最牢靠的硬件作为jobtracker jobtracker通过心跳(周期1分钟) 信号来了解TaskTracker 是否发生故障或者负载过重 jobtracker 将从任务节点列表中移除发生故障的TaskTracker 如果故障节点在执行map任务并且尚未完成,jobtracker 会要求其他节点 重新执行 次

最全Pycharm教程(23)——Pycharm编辑器功能之代码高亮显示及错误提示机制

1.代码高亮显示 当你在编辑框中输入代码时,Pycharm会在后台对其进行分析.这个IDE能够智能的识别出关键字.变量.字符串.注释等,并以不同的字体颜色进行显示.Pycharm的符号配色方案定义在 Colors and Fonts settings中(Ctrl+Alt+S→IDE Settings→Editor→Color and Fonts). 我们先选择编程语言,这里选择Python,然后根据个人习惯来设置字符配色方案.然而这里Pycharm自带的配色方案是不可更改的,我们需要先创建一个拷

PHP异常与错误处理机制

先区别一下php中错误 与 异常的概念吧 PHP错误:是属于php程序自身的问题,一般是由非法的语法,环境问题导致的,使得编译器无法通过检查,甚至无法运行的情况.平时遇到的warming.notice都是错误,只是级别不同而已. PHP异常:一般是业务逻辑上出现的不合预期.与正常流程不同的状况,不是语法错误. PHP异常处理机制借鉴了java  c++等,但是PHP的异常处理机制是不健全的.异常处理机制目的是将 程序正常执行的代码  与 出现异常如何处理的代码分离. PHP是无法自动捕获异常的(

Atitit 错误处理机制:(1)静默模式(2)警告模式 (3)异常模式

Atitit 错误处理机制:(1)静默模式(2)警告模式 (3)异常模式       三.     PDO的错误处理机制: (1)静默模式 默认情况下与mysql处理方式一致,不现实错误信息(静默模式)但是我们可以通过固定的方法获得错误信息 (2)警告模式 //更改属性设置错误处理模式 $pdo ->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_WARNING); (3)异常模式,当发生错误时,抛出一个异常 $pdo ->setAttribute(PD

javascript中的错误处理机制

前面的话 错误处理对于web应用程序开发至关重要,不能提前预测到可能发生的错误,不能提前采取恢复策略,可能导致较差的用户体验.由于任何javascript错误都可能导致网页无法使用,因此作为开发人员,必须要知道何时可能出错,为什么会出错,以及会出什么错.本文将详细介绍javascript中的错误处理机制 error对象 error对象是包含错误信息的对象,是javascript的原生对象.当代码解析或运行时发生错误,javascript引擎就会自动产生并抛出一个error对象的实例,然后整个程序