深入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主协程会立即结束。
欢迎大家关注微信公众号:技术人技术事,更多精彩期待你的到来
![](https://img2018.cnblogs.com/blog/706455/201909/706455-20190911210708072-261554801.jpg)
深入Go的错误处理机制使用的更多相关文章
- linux系统编程之错误处理机制
在讲解liunx错误处理机制之前我们先来看一段代码: #include<sys/types.h> #include<sys/stat.h> #include<fcntl. ...
- javascript中的错误处理机制
× 目录 [1]对象 [2]类型 [3]事件[4]throw[5]try[6]常见错误 前面的话 错误处理对于web应用程序开发至关重要,不能提前预测到可能发生的错误,不能提前采取恢复策略,可能导致较 ...
- Map/Reduce 工作机制分析 --- 错误处理机制
前言 对于Hadoop集群来说,节点损坏是非常常见的现象. 而Hadoop一个很大的特点就是某个节点的损坏,不会影响到整个分布式任务的运行. 下面就来分析Hadoop平台是如何做到的. 硬件故障 硬件 ...
- Android IOS WebRTC 音视频开发总结(七五)-- WebRTC视频通信中的错误恢复机制
本文主要介绍WebRTC视频通信中的错误恢复机制(我们翻译和整理的,译者:jiangpeng),最早发表在[这里] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:blac ...
- PHP异常与错误处理机制
先区别一下php中错误 与 异常的概念吧 PHP错误:是属于php程序自身的问题,一般是由非法的语法,环境问题导致的,使得编译器无法通过检查,甚至无法运行的情况.平时遇到的warming.notice ...
- ASP.NET的错误处理机制之一(概念)
对Web应用程序来说,发生不可预知的错误和异常在所难免,我们必须为Web程序提供错误处理机制.当错误发生时,我们必须做好两件事情:一是将错误信息记录日志,发邮件通知网站维护人员,方便技术人员对错误进行 ...
- ASP.NET的错误处理机制
对于一个Web应用程序来说,出错是在所难免的,因此我们应该未雨绸缪,为可能出现的错误提供恰当的处理.事实上,良好的错误处理机制正是衡量Web应用程序好坏的一个重要标准.试想一下,当用户不小心在浏览器输 ...
- hadoop 错误处理机制
hadoop 错误处理机制 1.硬件故障 硬件故障是指jobtracker故障或TaskTracker 故障 jobtracker是单点,若发生故障,目前hadoop 还无法处理,唯有选择最牢靠的硬件 ...
- .net错误处理机制(转)
asp.net 提供了4中错误机制:Page_Error事件>ErrorPage属性>Application_Error事件> <customErrors>配置项 ① P ...
- PHP中的错误处理机制
常见的三种错误: 1.Notice :通知性错误,最小的错误,当发生通知性错误时,会弹出一个提示信息.不会中断代码的执行. 错误代码: #例如Notice: 2.Warning:警告性错误,当发生警告 ...
随机推荐
- 数据结构之二叉树的构建C++版
二叉树的构建要注意与链式表的区别,二叉树这里的构建十分低级,每个树只是构建了一个单一的二叉树节点,总体来看是有下向上构建的.用户需要手动去构建自己需要的树,而不是直接去插入数据就到二叉树中了,因为不是 ...
- SpringBoot第一天
一,SpringBoot 介绍 1,如果使用 Spring 开发一个"HelloWorld"的 web 应用: • 创建一个 web 项目并且导入相关 jar 包.SpringMV ...
- Python基础总结之初步认识---class类的继承(终)。第十六天开始(新手可相互督促)
最近生病了,python更新要结束了,但是这才是真正的开始.因为后面要更新的是UnitTest单元测试框架,以及后续的Requests库.在后续的笔记会补充一些python的其他细节笔记.我想是这样的 ...
- pull解析案例
此pull解析案例是eclipes的对不对,不知道, private void getXml() { try { InputStream is = getAssets().open("new ...
- Java学习|强引用,软引用,弱引用,幻想引用有什么区别?
在Java语言中,除了基本数据类型外,其他的都是指向各类对象的对象引用:Java中根据其生命周期的长短,将引用分为4类. 1 强引用 特点:我们平常典型编码Object obj = new Objec ...
- Codeforces 343D Water Tree
题意简述 维护一棵树,支持以下操作: 0 v:将以v为跟的子树赋值为1 1 v:将v到根节点的路径赋值为0 2 v:询问v的值 题解思路 树剖+珂朵莉树 代码 #include <set> ...
- 爱奇艺JAVA后台面经
链接:https://www.nowcoder.com/discuss/217425 1.volatile关键字的含义 2.Java NIO 讲一下 2.1 NIO selector,epoll的区别 ...
- 纯数据结构Java实现(3/11)(链表)
题外话: 篇幅停了一下,特意去看看其他人写的类似的内容:然后发现类似博主喜欢画图,喜欢讲解原理. (于是我就在想了,理解数据结构的确需要画图,但我的文章写给懂得人看,只配少量图即可,省事儿) 下面正题 ...
- 第一次appium自动化
今天,自己独自做了一下app自动化,从搭环境到写好一个脚本花了很长时间.用的主要环境是python3.7+appium+sdk+夜神模拟器.appium环境搭建较于复杂,这里就不累述,参考百度教程. ...
- 常用Linux备份
用于备份的Tar 备份工具Tar是以前备份文件的可靠方法,几乎可以工作于任何环境中,Linux老用户一般都信赖它. Linux中以.tar结尾的文件都是用tar创建的.它的使用超出了单纯的备份,可用来 ...