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、返回值之間執行順序的坑

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM