zoukankan      html  css  js  c++  java
  • Golang 入门系列(十四)defer, panic和recover用法

    以前讲过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-------
    3
    2
    1
    0

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

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

  • 相关阅读:
    Codeforces 235C Cyclical Quest 后缀自动机
    HDU-6583 Typewriter 后缀自动机+dp
    HDU-4436 str2int 后缀自动机
    [题解]诸侯安置(dp+组合)
    [题解]洛谷P1350车的放置
    二进制表示中1的个数与异或关系
    [洛谷P1593][POJ-1845Sumdiv] 因子和
    [USACO08DEC]拍头Patting Heads 题解
    [小白逛公园]|[SP1716]|[UVA1400]解题报告(三合一)
    P2034选择数字题解
  • 原文地址:https://www.cnblogs.com/zhangweizhong/p/10999386.html
Copyright © 2011-2022 走看看