Go中的异常处理(基础)

news2024/7/6 20:50:07

Go 中异常处理

主要掌握 一下几个方面:

  1. 掌握error接口
  2. 掌握defer延迟
  3. 掌握panic及recover

error接口

error是指程序中出现不正常的情况,从而导致程序无法正常运行;

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.
type error interface {
	Error() string
}

可以看到接口中定义了Error方法,我们可以实现这个方法已实现error接口;

error本质上是一个接口类型,其中包含一个Error()方法,错误值可以存储在变量中,通过函数返回。它必须是函数返回的最后一个值。

在go中处理错误的方式通常是以下的形式:

if err!=nil{
fmt.println(err)
}

代码Demo


func Divide(a,b float64)(float64,error){
	if b==0{
		return -1 ,errors.New("by zero")
	}else {
		return a/b, nil
	}

}
func main() {
	divide, err := Divide(10, 0)
	if err!=nil{
		fmt.Println(err)
	}else {
		fmt.Println(divide)
	}
}

在这里插入图片描述

创建error对象

由于error是一个接口,因此只要实现该接口中的方法就可以:

结构体只要实现了Error() string这种格式的方法,就代表实现了该错误接口,返回值为错误的具体描述

在这之前我们先看一下go中提供了哪些关于error的方法:
在这里插入图片描述

简单看一下Join()的源码:

func Join(errs ...error) error {
	n := 0
	for _, err := range errs {
		if err != nil { //如果传进来的err不是 nil
			n++//n自增
		}
	}
	if n == 0 {
		return nil
	}
	e := &joinError{
		errs: make([]error, 0, n),
	}
	for _, err := range errs {
		if err != nil {
			e.errs = append(e.errs, err)
		}
	}
	return e
}

简单来讲就是err的拼接

func main() {
 	r, err := errorR(121)
	if err!=nil {
		fmt.Println(err)
		fmt.Printf("err的类型%T\n",err)
	}else {
		fmt.Println(r)
	}
}

func errorR(age int) (string, error) {

	if age < 0 || age > 120 {
		err := fmt.Errorf("输入的年龄%d不符合统计要求", age)
		return "", err
	} else {
		str := "您输入的年龄" + strconv.FormatInt(int64(age), 10) + "符合要求"
		return str, nil
	}
}

自定义错误

自定义错误的实现步骤如下。

  • • 定义一个结构体,表示自定义错误的类型。
  • • 让自定义错误类型实现error接口:Error()string。
  • • 定义一个返回error的函数。根据程序实际功能而定。

代码demo


//定义结构体

type errorDefine struct {
	reason string
}
//实现Error()方法
func (e errorDefine)Error()string  {

	return "除数为0,不符合要求/"+e.reason
}

func Divide2(a,b int)(float64,error){
	defineerr := errorDefine{reason: "by Zero"}

	if b==0{
		return -1,defineerr
	}else{
		return float64(a / b),nil
	}
}
func main() {
	divide2, err := Divide2(100, 0)
	if err!=nil {
		fmt.Println(err)
	}else {
		fmt.Println(divide2)
	}
}

defer 关键字

defer 是go中一个关键字,用于延迟一个函数或者方法(或者当前创建的匿名函数)的执行;

defer只能出现在函数或者方法的内部

defer的使用

在函数中可以添加多个defer语句。如果有很多调用defer,当函数执行到最后时,这些defer语句会按照逆序执行(报错的时候也会执行),最后该函数返回。

代码Demo

func DeferA(){
	println("这是deferA")
}
func DeferB(){
	println("这是deferB")
}
func DeferC(){
	println("这是deferC")
}

func main() {
	defer DeferA()
	DeferB()
	defer DeferC()

	fmt.Println("打印一些东西")
}

在这里插入图片描述

函数(整体)

defer 经常被用于**处理成对的操作,**如 打开-关闭连接,加锁解锁,等

func Divide(a, b float64) (float64, error) {
	if b == 0 {
		return -1, errors.New("by zero")
	} else {
		return a / b, nil
	}

}

func DeferB() {
	println("这是deferB")
}
func DeferC() {
	println("这是deferC")
}

func DeferFinish() {
	fmt.Println("运算结束")
}
func main() {
	divide, err := Divide(1024, 2)
	if err != nil {
		errors.New("发生了错误,请检查参数")

	}else{
		fmt.Printf("计算结果是%f\n",divide)
	}
	defer DeferFinish()
	DeferC()

	DeferB()
}

运行多次发现,defer 定义的方法DeferFinish 只能保证运行在Divide之后;

在这里插入图片描述

函数调用

以上defer是应用在函数内部,但是defer的使用 并不局限于函数,延迟一个方法的调用用也是可以的;

例如下面的例子:


func (f Fruit) FruitInfo() {
	fmt.Println(f.name, "--", f.color, "--", f.weight)

}

func main() {
	fruit := Fruit{
		name:   "樱桃",
		color:  "红",
		weight: 1.5,
	}

	defer fruit.FruitInfo()

	fmt.Println("我要吃樱桃")
	divide, _ := Divide(1024, 3)
	defer DeferFinish()
	fmt.Println(divide)
}

在这里插入图片描述

函数参数

延迟函数的参数在执行延迟语句时已被执行 而不是在执行实际的函数调用执行 时在执行;

什么意思呢,就是该函数的执行结果只是被延迟显示,换句话讲就是函数的参数已经被传递过来了;

看个例子就知道了;

func addNum(a, b int, flag rune) {

	if flag == 1 {
		fmt.Printf("延迟执行的函数 参数的值%d --%d ,和为 %d\n", a, b, a+b)
	} else {
		fmt.Printf("未延迟执行的函数 参数的值%d --%d ,和为 %d\n", a, b, a+b)

	}

}

func main() {
	a := 10
	b := 18
	flag := '1'
	//延迟调用addNum
	defer addNum(a, b, flag)
	//此时修改a b 的值
	a=100
	b=234
	flag='0'
	//再调用函数,看看被defer的函数的参数是否会受到再次赋值的影响
	addNum(a,b,flag)
}

看结果------

在这里插入图片描述
被延迟执行的参数 依照就近原则,在被defer定义时就已经确定了;

堆栈

当一个函数有多个延迟调用时,他们会被添加到一个堆栈中,按照LIFO的 顺序执行

例如我们要实现一个字符串的逆序输出:

一般的写法如下:

func ReversStr(str string) {
	//转换字节切片
	bytes := []byte(str)

	for i := len(bytes)-1; i >=0 ; i-- {
		fmt.Print(string(bytes[i]))
	}

}

func ReversStr2(str string){
	//转换字节切片
	r := []rune(str)
	for i := 0; i < len(r); i++ {
		defer fmt.Print(string(r[i]))
	}
	println()
}

func main() {
	ReversStr("hello world")
	ReversStr2("hello world")

}

panic和recover机制

go中异常处理机制跟java中不一样,go在设计时,设计者认为将异常于流程控制混在一起会让代码变得混乱;

go中panic()是一个内建的函数,可以中断原有的流程就像java中抛出异常一样;

代码demo

func PanicA() {
	fmt.Println("正常打印")
}
func PanicB() {
	fmt.Println("正常打印B")
	//参数是any 类型的
	panic("发生了异常中断了Panic")
}

func PanicC() {
	fmt.Println("我想要被正常打印")
}

func main() {
	PanicA()
	PanicB()//
	PanicC()

}

运行结果:

正常打印
正常打印B
panic: 发生了异常中断了Panic

goroutine 1 [running]:
main.PanicB(...)
	D:/GolandData/main.go:902
main.main()
	D:/GolandData/main.go:917 +0xa8

Process finished with the exit code 2

panic与error

通常情况下我们使用error就可以了,但是当遇到一些不可恢复的错误状态的时候,比如数组下标越界,空指针引用等,这些会引起panic,

不应通过调用panic()函数来报告普通的错误,而应该只把它作为报告致命错误的一种方式

func SliceAA(i int){
	var s [10]int
	s[i]=100

}
func main() {
	PanicA()
	PanicC()
SliceAA(11)
}

运行结果:

正常打印
我想要被正常打印
panic: runtime error: index out of range [11] with length 10

goroutine 1 [running]:
main.SliceAA(...)
	D:/GolandData/main.go:912
main.main()
	D:/GolandData/main.go:918 +0xa5

Process finished with the exit code 2

recover

panic一旦被引发就会导致程序崩溃,那怎么拦截呢?

go为开发者提供了内建函数recover(),该函数可以让goroutine恢复过来并重新获得流程控制权;

recover()让程序恢复,必须在延迟函数中执行,即 recover()只在延迟函数中生效;

正常的程序运行过程中,调用 recover()返回nil,并且没有其他任何效果
如果当前的Goroutine陷入恐慌,调用recover()可以捕获panic()的输入值,使程序恢复正常运行。

recover使用方式

代码Demo

1,自己的panic自己处理,不向上级抛

func PanicA() {
	fmt.Println("正常打印")
}
func PanicB() {
//自己内部处理
	defer func(){
		if msg:=recover();msg!=nil{
			fmt.Println("恢复中,获取的recover返回值:",msg)
		}
	}()
	fmt.Println("正常打印B")
	for i := 0; i < 10; i++ {
		fmt.Println(i)
		if i==5 {
			panic("有致命panic发生")
		}
	}
	//参数是any 类型的
	//panic("发生了异常中断了Panic")
}

func PanicC() {

	defer func(){
		fmt.Println("延迟执行函数")
		msg:=recover()
		fmt.Println("recover返回值",msg)
	}()
	fmt.Println("我想要被正常打印")
	panic("发生了panic")
}

func main() {
	PanicA()
	PanicB()
	PanicC()
	fmt.Println("over--over")
}

在这里插入图片描述

2,发生panic,谁调用谁处理

func PanicD(){
	fmt.Println("开始打印")

	for i := 0; i < 10; i++ {
		fmt.Println("i--",i)
		if i==5 {
			panic("i=5时发生致命错误")
		}
	}
}

func main() {
	defer func(){
		if msg:=recover();msg!=nil{
			fmt.Println("恢复程序执行,recover()返回值--",msg)
		}
	}()
	PanicD()
}

在这里插入图片描述

注意:这里的恢复执行是指的程序不中断,而不是指可以继续打印5之后的数字;

小结:recover()要在延迟函数中执行,可以专门定义一个延迟函数 来捕获panic 或者使用匿名函数来捕获;

一般情况下我们只需要考虑error,判断err是否为nil 就可以了,很少涉及到panic

代码中尽量少有或者没有panic异常,这是底线呀

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

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

相关文章

舵机云台实现追踪球形目标功能

1. 功能说明 在样机舵机云台上安装一个摄像头&#xff0c;本文示例将实现舵机云台追踪球形物体的功能。 2. 电子硬件 在这个示例中&#xff0c;我们采用了以下硬件&#xff0c;请大家参考&#xff1a; 主控板 Basra主控板&#xff08;兼容Arduino Uno&#xff09;‍ 扩展板 Bi…

Linux之创建进程、查看进程、进程的状态以及进程的优先级

文章目录 前言一、初识fork1.演示2.介绍3.将子进程与父进程执行的任务分离4.多进程并行 二、进程的状态1.进程的状态都有哪些&#xff1f;2.查看进程的状态2.运行&#xff08;R&#xff09;3.阻塞4.僵尸进程&#xff08;Z&#xff09;1.僵尸状态概念2.为什么要有僵尸状态&#…

Rust每日一练(Leetday0011) 下一排列、有效括号、搜索旋转数组

目录 31. 下一个排列 Next Permutation &#x1f31f;&#x1f31f; 32. 最长有效括号 Longest Valid Parentheses &#x1f31f;&#x1f31f;&#x1f31f; 33. 搜索旋转排序数组 Search-in-rotated-sorted-array &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷…

开发 Microsoft 365 Copilot Plugin!

大家好&#xff01;我是韩老师。 昨天凌晨的微软 Build 大会&#xff0c;大家都看了吗&#xff1f; 我看到了凌晨两点&#xff0c;且毫无困意&#xff01; 真的是干货满满~ 列举几个比较大的发布&#xff1a;1. Windows Copilot2. Bing 成为 ChatGPT 的默认搜索体验3. AI 插件生…

一起看 I/O | 移动设备、Web、AI 和 Google Cloud 更新一览

作者 / Developer X 副总裁兼总经理、开发者关系主管 Jeanine Banks 感谢您再次与我们共享 Google I/O 大会的精彩&#xff01;我们正在持续深度投入资源来提升 AI、移动设备、Web 和 Google Cloud&#xff0c;致力于让您的开发工作更加轻松。现在&#xff0c;您看到了许多我们…

实测「360智脑」的真正实力:能否领跑国内百“模”大战?

ChatGPT 的发布&#xff0c;无疑掀起了一股“AI 技术”新浪潮。百度文心一言、华为盘古、商汤日日新、阿里通义千问、讯飞星火等众多大模型的接连问世&#xff0c;使得国内的“百模之战”进入了前所未有的白热化阶段。无论是各大互联网巨头&#xff0c;还是清华、复旦等知名高校…

聊聊「短信」渠道的设计与实现

有多久&#xff0c;没有发过短信了&#xff1f; 一、背景简介 在常规的分布式架构下&#xff0c;「消息中心」的服务里通常会集成「短信」的渠道&#xff0c;作为信息触达的重要手段&#xff0c;其他常用的手段还包括&#xff1a;「某微」、「某钉」、「邮件」等方式&#xff…

多种群遗传算法的函数优化算法

以下内容大部分来源于《MATLAB智能算法30个案例分析》&#xff0c;仅为学习交流所用。 1 理论基础 1.1 遗传算法早熟问题 遗传算法是一种借鉴生物界自然选择和进化机制发展起来的高度并行、随机、自适应的全局优化概率搜索算法。由于优化时不依赖于梯度&#xff0c;具有很强…

拥抱新时代的Java

原文链接 拥抱新时代的Java Java作为面向对象编程的王牌语言&#xff0c;曾经风靡一时&#xff0c;在Web领域是绝对的老大。随着时间的推移&#xff0c;一些新的编程范式不断的涌现&#xff0c;如函数式编程&#xff0c;响应式编程&#xff0c;以及对函数的全力支持&#xff0…

北邮22信通:实验五 共射放大电路的频率特性与深负反馈的影响

北邮22信通一枚~ 很高兴以一个新身份与大家见面~ 关注作者&#xff0c;解锁更多邮苑模电实验报告~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22信通——电子电路_青山如墨雨如画的博客-CSDN博客 目录 实验目的&#xff1a; 实验设备及器件&#xff1a; …

区间预测 | MATLAB实现QGPR高斯过程分位数回归多变量时间序列区间预测

区间预测 | MATLAB实现QGPR高斯过程分位数回归多变量时间序列区间预测 目录 区间预测 | MATLAB实现QGPR高斯过程分位数回归多变量时间序列区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 MATLAB实现QGPR高斯过程分位数回归多变量时间序列区间预测 1.基于…

可用于Stable Diffusion免费的AI绘画标签生成器

这是一个开源项目&#xff0c;借花献佛&#xff0c;基于原作者的基础上进行了微调还有以及修改内容。 支持中英文&#xff0c;权重以及xxxx词汇&#xff0c;老司机都懂。 直接可在右侧根据需求进行点选操作&#xff0c;然后复制到自己的Stable Diffusion中进行AI绘画。 下载…

【产品经理】移动手机区别于PC端的10点特性

众所周知&#xff0c;当今时代已经是移动互联网时代。相比以前大家热衷于在电脑上浏览网页、玩游戏的情形&#xff0c;现在大家都喜欢通过移动手机去满足个人方方面面的需求&#xff0c;无论是购物、吃饭、求职、阅读、聊天、游戏等等。也就是说&#xff0c;在日常的生活中&…

ChatGPT变现赚钱 第二篇

前面我们已经介绍了一些利用ChatGPT做变现的一些玩法&#xff0c;比如做微信问一问的回答&#xff0c;做AI剪映的一键生成视频的(星球里面有详细的指南和视频)。 现在我们会员群在玩百度问一问答题&#xff0c;这个项目蛮好的&#xff0c;成本很低&#xff0c;现在又有苹果手机…

利用视听短片从自然刺激中获得开放的多模式iEEG-fMRI数据集

在认知神经科学领域&#xff0c;数据共享和开放科学变得越来越重要。虽然许多参与认知神经科学实验的志愿者的数据集现在是公开可用的&#xff0c;但颅内脑电图&#xff08;iEEG&#xff09;数据的共享相对较少。iEEG是一种高时间和空间分辨率的记录技术&#xff0c;通过在患者…

组长给组员派活,把组长自己的需求和要改的bug派给组员,合理吗?

组长把自己的工作派给手下&#xff0c;合理吗&#xff1f; 一位程序员问&#xff1a; 组长给他派活&#xff0c;把组长自己的需求或者要改的bug派给他。组长分派完需求之后&#xff0c;他一个人干两个项目&#xff0c;组长却无所事事&#xff0c;这样合理吗&#xff1f; 有人说…

微服务开发系列 第七篇:RocketMQ

总概 A、技术栈 开发语言&#xff1a;Java 1.8数据库&#xff1a;MySQL、Redis、MongoDB、Elasticsearch微服务框架&#xff1a;Spring Cloud Alibaba微服务网关&#xff1a;Spring Cloud Gateway服务注册和配置中心&#xff1a;Nacos分布式事务&#xff1a;Seata链路追踪框架…

华为OD机试真题 Java 实现【打印文件】【2023Q1 100分】

一、题目描述 有 5 台打印机打印文件&#xff0c;每台打印机有自己的待打印队列。 因为打印的文件内容有轻重缓急之分&#xff0c;所以队列中的文件有1~10不同的优先级&#xff0c;其中数字越大优先级越高。 打印机会从自己的待打印队列中选择优先级最高的文件来打印。 如果…

Linux常用命令——help命令

在线Linux命令查询工具 help 显示帮助信息 补充说明 help命令用于显示shell内部命令的帮助信息。help命令只能显示shell内部的命令帮助信息。而对于外部命令的帮助信息只能使用man或者info命令查看。 语法 help(选项)(参数)选项 -s&#xff1a;输出短格式的帮助信息。仅…

冯斌:突破认知临界点,打造自驱型团队 | 开发者说

熟悉冯斌的人&#xff0c;大都直接称呼其网名 Kid&#xff0c;包括他在 ONES 的同事。人如其名&#xff0c;Kid 的寓意就是「用孩子的眼光看世界」&#xff0c;返璞归真的思维方式才能发现新大陆。正如毕加索说的&#xff1a;「我一生都在向孩子学习。」 在 ONES 联合创始人兼 …