文章目录

  • 1.defer关键字
  • 2.defer特性、用途
  • 3.painc内置函数
  • 4.recover内置函数
  • 5.defer在ReadFile函数内关闭句柄
  • 6.defer在闭包中的应用(捕获变量被初始化赋值且修改)
  • 7.先return再defer
  • 8.defer在遍历执行结构体方法中的应用
  • 9.defer在遍历执行函数中的应用
  • 10.defer遍历切片输出索引
  • 11.自发panic
  • 12.使用defer自定义错误输出在panic之前
  • 13.使用panic
  • 15.使用recover

1.defer关键字

Go 语言中的类没有构造函数和析构函数的概念,处理错误和异常时也没有提供 try...catch...finally 之类的语法,那当我们想要在某个资源使用完毕后将其释放(网络连接、文件句柄等),或者在代码运行过程中抛出错误时执行一段兜底逻辑,要怎么做呢?
通过 defer 关键字声明兜底执行或者释放资源的语句可以轻松解决这个问题
注:defer是一个关键字,ctrl+左键后不会进行包的跳转

如果一条语句干不完清理的工作,也可以在 defer 后加一个匿名函数来执行对应的兜底逻辑:
defer func() {
// 执行复杂的清理工作…
} ()
另外,一个函数/方法中可以存在多个 defer 语句,defer 语句的调用顺序遵循先进后出的原则,即最后一个 defer 语句将最先被执行,相当于「栈」这个数据结构,如果在循环语句中包含了 defer 语句,则对应的 defer 语句执行顺序依然符合先进后出的规则。
由于 defer 语句的执行时机和调用顺序,所以我们要尽量在函数/方法的前面定义它们,以免在后面编写代码时漏掉,尤其是运行时抛出错误会中断后面代码的执行,也就感知不到后面的 defer 语句。

2.defer特性、用途

3.painc内置函数

func panic(v interface{})

注:panic函数是一个内置函数,在Builtin.go里面
前面介绍了 Go 语言通过 error 类型统一进行错误处理,但这些错误都是我们在编写代码时就已经预见并返回的,对于某些运行时错误,比如数组越界、除数为0、空指针引用,这些 Go 语言是怎么处理的呢?
Go 语言没有像 Java、PHP 那样引入异常的概念,也没有提供 try…catch 这样的语法对运行时异常进行捕获和处理,当代码运行时出错,而又没有在编码时显式返回错误时,Go 语言会抛出 panic,中文译作「运行时恐慌」,我们也可以将其看作 Go 语言版的异常

panic 函数支持的参数类型是 interface{}:
func panic(v interface{})
所以可以传入任意类型的参数:
panic(500) // 传入数字
panic(errors.New(“除数不能为0”)) // 传入 error 类型

4.recover内置函数

func recover() interface{}

recover函数是一个内置函数,在Builtin.go里面
我们还可以通过 recover() 函数对 panic 进行捕获和处理,从而避免程序崩溃然后直接退出,而是继续可以执行后续代码,实现类似 Java、PHP 中 try…catch 语句的功能。
由于执行到抛出 panic 的问题代码时,会中断后续其他代码的执行,所以,显然这个 panic 的捕获应该放到 defer 语句中完成,才可以在抛出 panic 时通过 recover 函数将其捕获,defer 语句执行完毕后,会退出抛出 panic 的当前函数,回调调用它的地方继续后续代码的执行。
可以类比为 panic、recover、defer 组合起来实现了传统面向对象编程异常处理的 try…catch…finally 功能。
这样一来,当程序运行过程中抛出 panic 时我们可以通过 recover() 函数对其进行捕获和处理,如果没有抛出则什么也不做,从而确保了代码的健壮性。

5.defer在ReadFile函数内关闭句柄

//比如我们看 Go 内置的 io/ioutil 包提供的读取文件方法 ReadFile 实现源码,其中就有 defer 语句的使用:
func ReadFile(filename string) ([]byte, error) {f, err := os.Open(filename)if err != nil {return nil, err}defer f.Close()var n int64 = bytes.MinReadif fi, err := f.Stat(); err == nil {if size := fi.Size() + bytes.MinRead; size > n {n = size}}return readAll(f, n)
}

6.defer在闭包中的应用(捕获变量被初始化赋值且修改)

7.先return再defer

8.defer在遍历执行结构体方法中的应用


9.defer在遍历执行函数中的应用

10.defer遍历切片输出索引

11.自发panic

//底层抛出 panic
package mainimport "fmt"func main() {var i = 1var j = 0var k = i / jfmt.Printf("%d / %d = %d\n", i, j, k)
}
/*
panic: runtime error: integer divide by zero
goroutine 1 [running]:
main.main()                                     D:/all project/go/demo/main/1.go:8 +0x11
*/

12.使用defer自定义错误输出在panic之前

//在这段代码中,我们定义了两个 defer 语句,并且是在函数最顶部,以确保异常情况下也能执行。
/*在函数正常执行的情况下,这两个 defer 语句会在最后一条打印语句执行完成后先执行第二条 defer 语句,再执行第一条 defer 语句:
*/
package mainimport "fmt"func printError()  {fmt.Println("兜底执行")
}func main()  {defer printError()defer func() {fmt.Println("除数不能是0!")}()var i = 1var j = 1var k = i / jfmt.Printf("%d / %d = %d\n", i, j, k)
}
/*输出以下三行:
1 / 1 = 1
除数不能是0!
兜底执行
*/
//底层抛出 panic
//而如果我们把 j 的值设置为 0,则函数会抛出 panic:
/*表示除数不能为零。这个时候,由于 defer 语句定义在抛出 panic 代码的前面,所以依然会被执行,底层的逻辑是在执行 var k = i / j 这条语句时,遇到除数为 0,则抛出 panic,然后立即中断当前函数 main 的执行(后续其他语句都不再执行),并按照先进后出顺序依次执行已经在当前函数中声明过的 defer 语句,最后打印出 panic 日志及错误信息*/
package mainimport "fmt"func printError() {fmt.Println("兜底执行")
}func main() {defer printError()defer func() {fmt.Println("除数不能是0!")}()var i = 1var j = 0var k = i / jfmt.Printf("%d / %d = %d\n", i, j, k)
}
/*
除数不能是0!
兜底执行
panic: runtime error: integer divide by zero
goroutine 1 [running]:
main.main()                                      D:/all project/go/demo/main/1.go:20 +0x46
*/

13.使用panic

/*没有通过 recover() 函数捕获 panic 的话,程序会直接崩溃退出,并打印错误和堆栈信息,但现在我们在 divide() 函数的 defer 语句中通过 recover() 函数捕获了 panic,并打印捕获到的错误信息,这个时候,程序会退出 divide() 函数而不是整个应用,继续执行 main() 函数中的后续代码,即恢复后续其他代码的执行:*/
package mainimport ("fmt"
)func divide() {defer func() {fmt.Println("222222222222")}()defer func() {fmt.Println("11111111")}()var i = 1var j = 0k := i / jfmt.Println(k)
}func main() {defer func() {fmt.Println("333333333333")}()divide()fmt.Println("divide 方法调用完毕,回到 main 函数")
}/*
11111111
222222222222
333333333333
panic: runtime error: integer divide by zerogoroutine 1 [running]:
main.divide()D:/ziliao/goproject/try/main.go:18 +0x46
main.main()D:/ziliao/goproject/try/main.go:26 +0x3f
*/

15.使用recover

recove必须搭配defer只用,因为recover是在程序可能出现异常之后才会去执行的
recover函数用来捕捉异常的信息

/*没有通过 recover() 函数捕获 panic 的话,程序会直接崩溃退出,并打印错误和堆栈信息,但现在我们在 divide() 函数的 defer 语句中通过 recover() 函数捕获了 panic,并打印捕获到的错误信息,这个时候,程序会退出 divide() 函数而不是整个应用,继续执行 main() 函数中的后续代码,即恢复后续其他代码的执行:
*/
package mainimport ("fmt"
)func divide() {defer func() {if err := recover(); err != nil {fmt.Printf("Runtime panic caught: %v\n", err)}}()var i = 1var j = 0k := i / jfmt.Printf("%d / %d = %d\n", i, j, k)
}func main() {divide()fmt.Println("divide 方法调用完毕,回到 main 函数")
}
/*输出:
Runtime panic caught: runtime error: integer divide by zero
divide 方法调用完毕,回到 main 函数
*/
/*如果在代码执行过程中没有抛出 panic,比如我们把 divide() 函数中的 j 值改为 1,则代码会正常执行到函数末尾,然后调用 defer 语句声明的匿名函数,此时 recover() 函数返回值为 nil,不会执行 if 分支代码,然后退出 divide() 函数回到 main() 函数执行后续代码*/
package mainimport ("fmt"
)func divide() {defer func() {if err := recover(); err != nil {fmt.Printf("Runtime panic caught: %v\n", err)}}()var i = 1var j = 1k := i / jfmt.Printf("%d / %d = %d\n", i, j, k)
}func main() {divide()fmt.Println("divide 方法调用完毕,回到 main 函数")
}
/*输出:
1 / 1 = 1
divide 方法调用完毕,回到 main 函数
*/
/*没有通过 recover() 函数捕获 panic 的话,程序会直接崩溃退出,并打印错误和堆栈信息,但现在我们在 divide() 函数的 defer 语句中通过 recover() 函数捕获了 panic,并打印捕获到的错误信息,这个时候,程序会退出 divide() 函数而不是整个应用,继续执行 main() 函数中的后续代码,即恢复后续其他代码的执行:*/
package mainimport ("fmt"
)func divide() {defer func() {if err := recover(); err != nil {fmt.Printf("Runtime panic caught: %v\n", err)}}()var i = 1var j = 0k := i / jfmt.Printf("%d / %d = %d\n", i, j, k)fmt.Println("111111111111")defer func() {fmt.Println("111111111111")}()
}func main() {divide()fmt.Println("divide 方法调用完毕,回到 main 函数")
}/*输出:
Runtime panic caught: runtime error: integer divide by zero
divide 方法调用完毕,回到 main 函数
*/

GoLang之defer、panic、recover相关推荐

  1. go defer,panic,recover详解 go 的异常处理

    golang中defer,panic,recover是很常用的三个特性,三者一起使用可以充当其他语言中try-catch-的角色,而defer本身又像其他语言的析构函数 defer defer后边会接 ...

  2. Go的异常处理 defer, panic, recover

    Go的异常处理 defer, panic, recover 参考文章: (1)Go的异常处理 defer, panic, recover (2)https://www.cnblogs.com/ghj1 ...

  3. Go案例说明defer panic recover

    defer推迟  recover恢复  panic恐慌 通过一个案例解释:发送邮件的 发送不成功要修改数据表的邮件发送状态值 发送邮件的内部sendEmail出现错误  要去修改数据表的状态值了 发送 ...

  4. golang中defer和recover的使用

    defer 内建函数,延迟调用,所在函数退出时调用,一个方法里若有多个defer语句,则先声明的后被调用,一般与recover()函数一起配合使用,recover()一般用于捕捉panic抛出的异常, ...

  5. Golang 错误捕获 Panic 与 Recover

    Golang 错误捕获 Panic 与 Recover,我抓住你了,Error Golang轻松学习 文章目录 Golang 错误捕获 Panic 与 Recover,我抓住你了,Error 一.Go ...

  6. Go实战--golang中defer的使用

    原址 生命不止,继续 go go go !!! 学习golang这么久了,还没看到类似传统的 try-catch-finally 这种异常捕捉方式.  但是,Go中引入的Exception处理:def ...

  7. golang panic recover return defer的逻辑顺序问题

    package mainimport "fmt"//验证golang return defer recover 之间的顺序关系func main() {defer func() { ...

  8. 在golang中defer、panic与recover的作用

    package mainimport "fmt"func main() {var s strings = "panic"fmt.Printf("a的初 ...

  9. golang中的panic和recover

    golang中的panic需要recover捕获,不然程序就会挂掉 package mainimport "fmt"func main() {f1()fmt.Println(&qu ...

  10. Golang中的panic和recover(捕获异常)

    Golang中的panic和recover(捕获异常) 参考文章: (1)Golang中的panic和recover(捕获异常) (2)https://www.cnblogs.com/zhzhlong ...

最新文章

  1. 一人之力,刷爆三路榜单!信息抽取竞赛夺冠经验分享
  2. 只适合小模型小训练集的交叉验证
  3. 是知当代之士、驰骛之曹,书读纵横,则思诸侯之变
  4. AC日记——[HNOI2008]玩具装箱toy bzoj 1010
  5. 【温故知新】CSS学习笔记(后代和子代选择器)
  6. 华为交换机-端口由trunk改为access
  7. weblogic概览下的上下文根配置_weblogic创建域
  8. windows下面虚拟主机
  9. delphi 文字 动画 特效 控件_设计师的特效让程序员追砍3条街,220集AE软件教学视频,教他做人...
  10. 【Selenium2】【Shell】
  11. 关于MCNS/DOCSIS兼容RF接口的RF接口MIB
  12. 东方联盟郭盛华发家史:8年来实现跨越式发展
  13. activity多实例任务节点审批
  14. 互联网+大赛作品_“颂中国力量 绘美好梦想”全市中小学生互联网+书画大赛作品展示(四)...
  15. 深入理解散列函数和散列表
  16. HPE矛头直指思科 与Arista达成软件定义合作关系及销售协议
  17. Box2d源码学习十二b2Collision之碰撞(上)公共部分的实现
  18. Kafka rebalance触发条件
  19. Excel中VLOOKUP函数的详细用法(灰常有用,求加精!求加精!)
  20. 太阳能发电系统的构成及简单工作原理

热门文章

  1. vue element-ui el-upload去除按delete 键可删除提示
  2. 使用Sbo用户自定义业务对象
  3. DNS是什么?工作原理、工作流程总结
  4. 龙芯OS看cpu频率
  5. oracle现金流量表逻辑,【实战】编制现金流量表的一些个人理解(附下载勾稽表)...
  6. 简单的C语言实训代码
  7. 第二周博客作业西北师范大学|李晓婷
  8. DELL服务器 RAID 配置详解
  9. 【历史上的今天】3 月 2 日:雅虎正式成立;PC 设计先驱诞生;Excite@Home 破产
  10. Unity导出exe时遇到的两个问题