【Golang笔记03】error、panic、fatal错误处理学习笔记

Golang笔记:错误处理学习笔记

一、进阶学习

1.1、错误(异常处理)

Go语言中也有和Java中的异常处理相关的机制,不过,在Go里面不叫异常,而是叫做:错误。错误分为三类,分别是:

  • error:部分流程错误,需要程序员进行处理,这种级别的错误,不会导致整个程序停止运行。
  • panic:严重错误,等程序执行完成之后,会立即退出运行。
  • fatal:致命错误,整个程序立即停止运行。

Go语言中,不存在try..catch相关的语句,Go中的错误是通过返回值的形式返回的,例如:在调用一个方法的时候,这个方法可以增加一个返回值,表示是否存在错误信息,然后通过if条件语句判断,是否能够正常执行后续代码。

上面这种错误的处理方式,虽然简化了try...catch的使用,但是,引入了if条件语句,也会导致代码里面出现一大堆的条件判断,所以,从本质上来说,还是没有简化的,只不过是采用另一种方式处理错误而已。

1.1.1、error错误

Go中有一个error接口,接口里面只有一个Error()方法,返回值是一个string字符串,这个字符串表示错误信息。Go中创建error错误,可以通过下面两种方式:

  • 第一种方式:使用errors包下的New()函数。
  • 第二种方式:使用fmt包下的Errorf()函数。
package mainimport ("errors""fmt"
)// 返回值第二个参数是 error 错误
func div(a int, b int) (int, error) {if b == 0 {// 定义 error 级别的错误返回return -1, errors.New("分母不能为0")// 或者使用下面这种方式// return -1, fmt.Errorf("分母不能为0")}c := a / breturn c, nil
}func main() {i, err := div(1, 0)if err != nil {fmt.Println(err)return}fmt.Println("结果=", i)
}
1.1.2、自定义error错误

Go中也允许程序开发人员自定义error级别的错误。自定义错误很简单,只需要对应的类型,实现了Error()方法,那么这个类型就将被看作是error接口的实现类,也就属于error类型的错误了。

自定义error错误的步骤,如下所示:

  • 第一步:自定义一个类型。
  • 第二步:实现Error()方法,返回string错误字符串信息。
  • 第三步:创建自定义类型对象,作为error错误即可。

案例代码,如下所示:

package mainimport ("fmt"
)// 第一步:MyCustomError 自定义错误类型
type MyCustomError struct {code intmsg  string
}// 第二步:实现 error 接口的 Error() 方法
func (e MyCustomError) Error() string {// 自定义错误的返回格式return fmt.Sprintf("自定义错误:code=%d, msg=%s", e.code, e.msg)
}func main() {// 第三步:创建自定义错误customError := MyCustomError{code: 1,msg:  "分母不能为0",}fmt.Println(customError)
}
1.1.3、Unwrap解包错误

Go语言中,允许错误嵌套,一个错误嵌套一个错误,从而形成一个错误链表。要想从一个错误中,获取它包含的错误,可以调用errors中提供的Unwrap()方法。

  • Unwrap()方法:从当前error错误中,获取其内部包含的error错误,如果内部不存在error错误,则返回nil;如果存在,则返回内部的error错误。

需要注意的是,Unwrap()方法获取到的error错误,有可能也是嵌套的error错误,如果想要获取指定的error错误,可以使用递归的方式进行判断。

package mainimport ("errors""fmt"
)// MyCustomError 自定义错误类型
type MyCustomError struct {code intmsg  string
}// 实现 error 接口的 Error() 方法
func (e MyCustomError) Error() string {// 自定义错误的返回格式return fmt.Sprintf("自定义错误:code=%d, msg=%s", e.code, e.msg)
}func main() {// 创建自定义错误customError := MyCustomError{code: 1,msg:  "分母不能为0",}// 嵌套错误,形成一个错误链err := fmt.Errorf("错误:%w", customError)fmt.Println(err)// 解包错误err = errors.Unwrap(err)fmt.Println(err)// 解包错误err = errors.Unwrap(err)fmt.Println(err)
}
// 执行结果
错误:自定义错误:code=1, msg=分母不能为0
自定义错误:code=1, msg=分母不能为0
<nil>

从上面可以看到,errors.Unwrap()方法的作用就是从错误链中解包内部错误。

1.1.4、检查错误

Go语言中,判断某个error错误是否为指定类型,可以使用errors.Is()方法,这个方法的作用是:判断当前error是否为目标target类型的错误,如果是目标类型的错误,则返回true,否则返回false

  • errors.Is(err, targer error) bool方法:判断是否为目标错误类型。
package mainimport ("errors""fmt"
)var originalErr = errors.New("this is an error")// 包裹原始错误
func wrap1() error {return fmt.Errorf("wrapp error %w", wrap2())
}// 原始错误
func wrap2() error {return originalErr
}func main() {err := wrap1()// 如果使用if err == originalErr 将会是falseif errors.Is(err, originalErr) {fmt.Println("original")}
}

另外,Go中还提供了一个errors.As(err, target Any) bool方法,这个方法的作用是:在当前错误链中寻找第一个匹配target的错误类型,并且将找到的错误赋值给传入的err变量。

  • errors.As(err, target Any) bool方法:用于检查一个错误是否可以转换成目标,如果可以就会将目标错误赋值给第一个参数err
package mainimport ("errors""fmt""time"
)// TimeError 自定义error
type TimeError struct {Msg string// 记录发生错误的时间Time time.Time
}// 实现 error 接口方法
func (m TimeError) Error() string {return m.Msg
}// NewMyError 定义自定义错误函数,并且使用 & 符号,返回一个 结构体指针 类型
func NewMyError(msg string) error {// 使用 & 符号,返回一个 结构体指针 类型,即:返回内存地址return &TimeError{Msg:  msg,Time: time.Now(),}
}// 包裹原始错误
func wrap1() error {return fmt.Errorf("wrapp error %w", wrap2())
}// 原始错误
func wrap2() error {return NewMyError("original error")
}func main() {// 定义一个 结构体指针 变量var myerr *TimeError// 获取错误err := wrap1()// 检查错误链中是否有 *TimeError 类型的错误if errors.As(err, &myerr) { // 输出TimeError的时间fmt.Println("original", myerr.Time)}
}

1.2、panic

前面学的error错误,是不严重的错误类型,如果不在后面写return语句,那么程序还是会继续往后执行下去的。Go语言中还提供了一个panic错误。

panic类型是一种比error更加严重的错误类型,当出现panic之后,后续的代码时不会继续执行,即使没有使用return关键字,代码也不会执行panic后面的代码。

注意:panic你可以理解成是Java语言中的使用throw new抛出异常的模式。

1.2.1、创建panic

Go中提供了一个panic函数,通过这个函数可以创建panic错误。

func panic(v any)

我对panic的理解是,panic就相当于是简写了Java语言中的throw new Exception("错误信息")的语句。例如:

// 在Java语言中,抛出异常
throw new Exception("错误异常信息")// 而在Go里面,只需要写一个 panic 函数即可
panic("错误异常信息")

使用panic的时候,需要注意的是,panic后面的代码是不会执行的。

1.2.2、panic善后

当发生panic错误之前,如果存在defer延迟函数,那么程序首先会依次执行defer延迟函数,执行完成之后,才会触发panic

package mainimport "fmt"func mockPanic() {fmt.Println("开始执行panic...")panic("模拟panic错误...")fmt.Println("panic执行结束...")
}
func demo01() {fmt.Println("执行demo01()函数...")
}
func demo02() {fmt.Println("执行demo02()函数...")
}
func demo03() {fmt.Println("执行demo03()函数...")
}func main() {fmt.Println("Hello")defer demo01()defer demo02()mockPanic()defer demo03()fmt.Println("World")
}// 程序运行结果
Hello
开始执行panic...
执行demo02()函数...
执行demo01()函数...
panic: 模拟panic错误...goroutine 1 [running]:
main.mockPanic()D:/environment/GoWorks/src/go-study/Hello.go:7 +0x59
main.main()D:/environment/GoWorks/src/go-study/Hello.go:24 +0x7d

当发生panic时,会立即停止当前函数的执行,然后执行善后的一些操作,例如:执行defer延迟函数;执行完成之后,会将panic向上层函数传递,上层函数也会进行相应的善后操作,直到main函数终止运行。

1.2.3、恢复执行

当使用panic之后,程序会终止运行,如果我们想恢复程序的执行,那么可以使用recover()内置函数来实现。需要注意的是,recover()函数必须在defer延迟函数里面使用。另外,recover()函数的返回值是panic函数中的错误信息内容。

package mainimport ("fmt"
)func demo() {fmt.Println("C")defer func() {fmt.Println("D")// 恢复程序执行,就相当于是取消panicok := recover()if ok != nil {fmt.Println("恢复程序执行")}fmt.Println("E")}()// 发生 panic 则 demo 方法终止执行panic("模拟panic错误...")fmt.Println("F")
}func main() {fmt.Println("A")demo()fmt.Println("B")
}// 执行结果
A
C
D
恢复程序执行
E
B

从上面代码中,可以发现,正常情况下,发生panic之后,如果没有使用recover()函数恢复执行,那么最终的输出结果将只有下面这些内容:

A
C
D
E
panic: 模拟panic错误...goroutine 1 [running]:
main.demo()D:/environment/GoWorks/src/go-study/Hello.go:19 +0x76
main.main()D:/environment/GoWorks/src/go-study/Hello.go:25 +0x4f

但是,我们在demo()函数中,定义了一个defer延迟函数,并且在里面使用recover()函数恢复程序的执行,也就相当于是捕获处理了panic,那么demo()函数中的panic就不会向上传递到main函数里面,所以main函数中的代码将正常执行。

理解这一点很重要,因为如果把上面代码改成下面执行顺序,如下所示:

package mainimport ("fmt"
)func demo() {fmt.Println("C")// 发生 panic 则 demo 方法终止执行panic("模拟panic错误...")fmt.Println("F")
}func main() {fmt.Println("A")// 在main函数里面定义deferdefer func() {fmt.Println("D")// 恢复程序执行,就相当于是取消panicok := recover()if ok != nil {fmt.Println("恢复程序执行")}fmt.Println("E")}()demo()fmt.Println("B")
}// 执行结果
A
C
D
恢复程序执行
E

上面代码中,从输出结果来看,程序没有执行demo()函数之后的代码,这是为什么呢???

  • 我是这么理解的,当demo()函数中,发生panic之后,由于demo()函数没有进行处理,所以会向上传递到main()函数里面。
  • main()函数发现,此时发生了panic,所以就不会执行后面的代码。
  • 此时触发panic的善后相关代码,例如:执行defer函数。
  • 首先,会从demo()函数中,依次开始执行defer函数,然后向上传递到main函数里面,开始执行main函数中的defer函数。
  • defer函数执行完成之后,此时整个程序就结束运行了。
  • 所以,最终demo()函数之后的代码,也就不会再执行了。

下面举个例子,来看看发生panic之后,程序的输出结果分别是多少,如下所示:

package mainimport ("fmt"
)func demo() {fmt.Println("B1")defer fmt.Println("C")defer func() {fmt.Println("E1")// 恢复程序执行,就相当于是取消panicok := recover()if ok != nil {fmt.Println("恢复程序执行")}fmt.Println("E2")}()demo02()fmt.Println("B2")
}func demo02() {fmt.Println("D1")panic("demo02函数发生panic...")fmt.Println("D2")
}func main() {fmt.Println("A1")demo()fmt.Println("A2")
}// 输出结果
// A1 B1 D1 E1 恢复... E2 C A2

看到这里,你学会了吗???

使用recover()函数,有四个注意事项:

  • 必须在defer函数中使用recover()函数。
  • 多次使用recover()函数,只会恢复一个panic
  • 闭包结构中使用recover()函数,不能恢复外部函数的panic
  • panic参数禁止使用nil

可以将recover()函数理解成是Java中的try...catch的功能,也就是捕获panic错误,然后能够让程序继续正常执行。

1.3、fatal

fatalGo语言中的致命错误,这种错误一旦发生,那么整个程序就会立即停止运行,fatal是没有办法进行善后操作的,也就是说,发生fatal之后,程序都来不及执行defer函数。

package mainimport ("fmt""os"
)func main() {fmt.Println("A1")// 模拟 fatal 错误os.Exit(1)fmt.Println("A2")// A1 B1 D1 E1 恢复 E2 C A2
}

Go语言中一般不会显示的声明fatal错误,发生fatal错误一般都是程序自主发生的,不是人为干预的。

注意:Go语言中一般使用os.Exit(1)代码来实现fatal错误。

以上,就是Go语言中错误处理相关的知识点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/84095.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python可视化设计原则

在数据驱动的时代&#xff0c;可视化不仅是结果的呈现方式&#xff0c;更是数据故事的核心载体。Python凭借其丰富的生态库&#xff08;Matplotlib/Seaborn/Plotly等&#xff09;&#xff0c;已成为数据可视化领域的主力工具。但工具只是起点&#xff0c;真正让图表产生价值的&…

​​WPF入门与XAML基础:从零开始构建你的第一个WPF应用​

从零开始构建你的第一个WPF应用​ 1.什么是WPF&#xff1f;​​2.开发环境搭建​​2.1 安装Visual Studio​​2.2 创建第一个WPF项目​​ 3. WPF项目结构解析​​​​3.1 MainWindow.xaml​​3.2 MainWindow.xaml.cs​​ 4. XAML基础语法​​4.1 属性赋值方式​​4.2 命名空间&…

电子电气架构 --- 下一代汽车电子电气架构中的连接性

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…

学习日记-day13-5.22

完成目标&#xff1a; 知识点&#xff1a; 1.抽象注意事项 知识点 核心内容 重点 抽象类实例化限制 抽象类不能直接实例化对象&#xff0c;只能创建非抽象子类的对象 尝试实例化抽象类会触发编译错误 抽象方法与抽象类关系 抽象类不一定包含抽象方法&#xff0c;但含…

华硕无畏Pro14 2025,打造舒适办公新体验

在快节奏、高效率的现代办公环境中&#xff0c;一台得心应手的笔记本无疑是每位职场人士的“第二大脑”与核心生产力工具。它不仅需要承载日常工作的繁杂事务&#xff0c;更要在关键时刻稳定输出&#xff0c;助力我们从容应对各种挑战。 洞悉此需求&#xff0c;华硕推出了全新…

重写B站(网页、后端、小程序)

1. 网页端 1.1 框架 Vue ElementUI axios 1.2 框架搭建步骤 搭建Vue 1.3 配置文件 main.js import {createApp} from vue import ElementUi from element-plus import element-plus/dist/index.css; import axios from "axios"; import router from…

MySQL数据 在 磁盘上是什么样子的

MySQL数据 在 磁盘上是什么样子的&#xff0c;取决于所使用的存储引擎。存储于引擎 是作用在 表! 上的。 存储引擎 百度百科是这样定义存储引擎的&#xff1a;MySQL 中的数据用各种不同的技术存储在文件&#xff08;或者内存&#xff09;中&#xff0c;这些不同的技术以及配套…

MySQL的相关操作

目录 一. 字符串函数 二. group by分组 2.1 作用 2.2 格式 2.3 举例 三. order by排序 3.1 格式 3.2 举例 四. limit 4.1 作用 4.2 举例 五. having 5.1 作用 5.2 举例 六. 正则表达式 七. 多表查询 7.1 定义 7.2 子查询 7.3 联合查询 纵向合并 7.4 交叉连…

网络安全-等级保护(等保) 2-7-3 GB/T 25058—2019 第7章 安全设计与实施

############################################################################### 对于安全厂家而言&#xff0c;最关心的内容在本章节&#xff0c;根据已确定的安全总体方案&#xff0c;完成技术措施和管理措施的详细设计和实施&#xff0c;包含具体的安全产品和管理要求。…

【Spring Boot】配置实战指南:Properties与YML的深度对比与最佳实践

目录 1.前言 2.正文 2.1配置文件的格式 2.2properties 2.2.1基础语法 2.2.2value读取配置文件 2.2.3缺点 2.3yml 2.3.1基础语法 2.3.2配置不同数据类型 2.3.3配置读取 2.3.4配置对象和集合 2.3.5优缺点 2.4综合练习&#xff1a;验证码案例 2.4.1分析需求 2.4.2…

20250519使用TF卡将NanoPi NEO core开发板刷机为Ubuntu core22.04.3系统完成之后执行poweroff自动关机

1、h3-sd-friendlycore-xenial-4.14-armhf-20210618.img.gz 在WIN10下使用7-ZIP解压缩/ubuntu20.04下使用tar 2、Win32DiskImager.exe 写如32GB的TF卡。【以管理员身份运行】 3、TF卡如果已经做过会有3个磁盘分区&#xff0c;可以使用SD Card Formatter/SDCardFormatterv5_WinE…

编译Qt5.15.16并启用pdf模块

编译Qt5.15.16并启用pdf模块 标题1.目录设置 -q-bulid –qt-everywhere-src-5.15.16 –bulid cd bulid 必须&#xff0c;否则会提示Project ERROR: You cannot configure qt separately within a top-level build. create .qmake.stash and .qmake.super in build folder …

“智”斗秸秆焚烧,考拉悠然以科技之力筑牢生态安全防线

清晨&#xff0c;薄雾笼罩着辽阔的田野&#xff0c;农民们开始了一天的劳作。然而&#xff0c;随着收割季的到来&#xff0c;秸秆焚烧问题也逐渐浮现&#xff0c;成为威胁空气质量与生态安全的隐患。传统监管方式往往显得力不从心&#xff0c;效率低下的困境亟待突破。在此背景…

Nockchain项目部署教程

Nockchain头矿窗口正在打开&#xff0c;不拼设备&#xff0c;现在部署&#xff0c;马上就要开挖了。 一、项目介绍 Nockchain 是结合了POW和ZKVM的区块链协议。 主要特点&#xff1a; 1&#xff09;计算存储新域名空间三位一体架构&#xff0c;高吞吐量 2&#xff09;使用No…

2025年气候持续大风,消纳减少,如何保收益?东润能源整体解决方案持续保收益保增长

引言 随着全球气候变化加剧,2025年极端天气频发,风资源丰富但电网消纳能力不足的问题日益突出,导致许多风电项目面临限电、收益下滑的挑战。如何在复杂的气候和电力市场环境下保障投资收益,成为行业关注的焦点。东润能源凭借领先的技术创新和综合能源解决方案,为风电行业…

2023河南CCPC省赛vp部分补题

A 模拟 暴力 对每个合法的前缀&#xff0c;判断后缀是不是合法 int a[29]; void solve(){string s;cin>>s;int t-1;if(s.size()1){return cout<<"NaN"<<endl,void();}for(int i0;i<27;i) a[i]0;for(int i0;i<s.size();i){a[s[i]-a];if(…

【2025保姆级】Open-WebUI五大功能区首曝!第一篇:管理员面板深度拆解,手把手讲解配置AI管理中枢

【2025保姆级】Open-WebUI五大功能区首曝&#xff01;第一篇&#xff1a;管理员面板深度拆解&#xff0c;手把手讲解&配置AI管理中枢 一、引言二、用户2.1 概述2.2 权限组 三、竞技场评估四、函数五、设置5.1 通用5.1.1 身份验证5.1.2 功能 5.2 外部连接5.2.1 OpenAI API5.…

docker上传镜像

向Docker Hub上传镜像&#xff0c;需要按照一定的步骤进行操作。 Docker Hub是Docker的官方镜像仓库&#xff0c;用户可以在其中存储、管理和部署Docker镜像。要向Docker Hub上传镜像&#xff0c;请遵循以下步骤&#xff1a; 创建Docker Hub账户&#xff1a; 访问Docker Hub官…

(十三)深入了解AVFoundation-采集:视频帧采集与实时滤镜处理

引言 在移动应用中&#xff0c;实时视频处理已成为视频拍摄、短视频、直播、美颜相机等功能的核心技术之一。从简单的滤镜叠加&#xff0c;到复杂的美颜、AR 特效&#xff0c;背后都离不开对每一帧图像的高效采集与处理。在前几篇文章中&#xff0c;我们已经实现了基本的视频采…

数字政务安全实战:等保2.0框架下OA系统防护全解析

近期在Python基础教学领域深入钻研函数机制、数据结构优化等内容时&#xff0c;深刻意识到信息安全作为技术基石的战略价值。在政务数字化转型浪潮中&#xff0c;Python凭借其高扩展性与丰富的安全生态库&#xff0c;成为构建政务OA系统安全防护体系的核心工具。本文将以等保2.…