defer, panic和recover用法【Golang 入门系列十四】

mb5fe94b3e552d9 · · 515 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

以前讲过golang 的基本语法。但是,只是讲了一些基础的语法,感兴趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.html,前段时间有人问我defer,recover的用法。所以,还是统一的总结一下相关的关键字吧。

 

其实,Go语言是不支持 try…catch…finally 这种异常处理的,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,甚至一个小小的错误都抛出一个异常。

在Go语言中,使用多值返回来返回错误。不要用异常代替错误,更不要用来控制流程。在极个别的情况下,才使用Go中引入的Exception处理:defer, panic, recover。

 

一. defer 用法

defer的特性: 在函数返回之前, 调用defer函数的操作, 简化函数的清理工作.

在初接触到go时, 就被defer吸引住了,但是在使用defer关键字的时候,还是得注意这些:

1. 在defer表达式确定的时候,defer修饰的函数(后面统称为defered函数)的参数也就确定了

package main
import (   "fmt")
func main() {   g()}
func g() {   i := 0   defer fmt.Println(i)   i++   return}-------output-------0

2. 函数内可以有多个defered函数,但是这些defered函数在函数返回时遵守后进先出的原则

package main
import "fmt"
func main() {   g()}
func g() {   for i := 0; i<4; i++ {      defer fmt.Println(i)   }}-------output-------3210

 

3. 函数命名的返回值跟defered函数一起使用
函数的返回值有可能被defer更改,本质原因是return xxx语句并不是一条原子指令,执行过程是: 保存返回值(若有)-->执行defer(若有)-->执行return跳转。

func f() (result int) {    defer func() {        result++    }()    return 0}
func g() (r int) {    t := 5    defer func() {        t = t + 5    }()    return t}
func h() (r int) {    defer func(r int) {        r = r + 5    }(r)    return 1}-------output-------0

对于defered函数跟函数命名返回值一块使用的情况, 当无法判断返回值的时候, 需要对函数进行变形.

func f(result int) {    result = 0    func () {        result++    }()    return}     -------output-------1
func g() (r int) {    t := 5    r = t    func () {        t = t + 5    }    return}-------output-------5
func h() (r int) {    r = 1    func (r int) {        r = r + 5    }(r)    return}-------output-------1

在func(r int) {...}中,由于r是以值传递的方式进行的, 所以r的值不会改变。

 

注意:
  1. 申请资源后最好立即使用defer关闭资源。


二. panic用法

panic用法挺简单的, 其实就是throw exception。

panic是golang的内建函数,panic会中断函数F的正常执行流程, 从F函数中跳出来, 跳回到F函数的调用者. 对于调用者来说, F看起来就是一个panic, 所以调用者会继续向上跳出, 直到当前goroutine返回. 在跳出的过程中, 进程会保持这个函数栈. 当goroutine退出时, 程序会crash。

要注意的是, F函数中的defered函数会正常执行, 按照上面defer的规则。

同时引起panic除了我们主动调用panic之外, 其他的任何运行时错误, 例如数组越界都会造成panic

看下面一个例子

package main
import (    "fmt")
func main() {    test()}
func test() {    defer func() { fmt.Println("打印前") }()    defer func() { fmt.Println("打印中") }()    defer func() { fmt.Println("打印后") }()    panic("触发异常")    fmt.Println("test")}
-------output-------打印后 打印中 打印前 panic: 触发异常 goroutine 1 [running]: main.test()     D:/Go_Path/go/src/logDemo/main.go:15 +0x98     main.main() D:/Go_Path/go/src/logDemo/main.go:8 +0x27 exit status 2


三. recover 用法
recover也是golang的一个内建函数, 其实就是try catch。

不过需要注意的是:

  1. recover如果想起作用的话, 必须在defered函数中使用。
  2. 在正常函数执行过程中,调用recover没有任何作用, 他会返回nil。如这样:fmt.Println(recover()) 。
  3. 如果当前的goroutine panic了,那么recover将会捕获这个panic的值,并且让程序正常执行下去。不会让程序crash。

func main() {   fmt.Println("c")   defer func() { // 必须要先声明defer,否则不能捕获到panic异常      fmt.Println("d")      if err := recover(); err != nil {         fmt.Println(err) // 这里的err其实就是panic传入的内容      }      fmt.Println("e")   }()   f() //开始调用f   fmt.Println("f") //这里开始下面代码不会再执行}
func f() {   fmt.Println("a")   panic("异常信息")   fmt.Println("b") //这里开始下面代码不会再执行}
-------output-------cad异常信息e



有疑问加站长微信联系(非本文作者)

本文来自:51CTO博客

感谢作者:mb5fe94b3e552d9

查看原文:defer, panic和recover用法【Golang 入门系列十四】

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

515 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传