Golang 入门系列(十四)defer, panic和recover用法

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

 

其实,Go语言是不支持 try…catch…finally 这种异常处理的,由于Go语言的设计者们认为,将异常与控制结构混在一块儿会很容易使得代码变得混乱。由于开发者很容易滥用异常,甚至一个小小的错误都抛出一个异常。
在Go语言中,使用多值返回来返回错误。不要用异常代替错误,更不要用来控制流程。在极个别的状况下,才使用Go中引入的Exception处理:defer, panic, recover。
 

一. defer 用法git

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

在初接触到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函数在函数返回时遵照后进先出的原则spa

package main

import "fmt"

func main() {
g()
}

func g() {
for i := 0; i<4; i++ {
defer fmt.Println(i)
}
}
-------output-------
3
2
1
0

 

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

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的值不会改变。code

 

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


二. 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------- c a d 异常信息 e

 

参考连接
  1. defer关键字
  2. Golang中defer、return、返回值之间执行顺序的坑

相关文章
相关标签/搜索