<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go 錯(cuò)誤處理篇(三):panic 和 recover

          共 3366字,需瀏覽 7分鐘

           ·

          2021-03-14 13:28

          前面學(xué)院君介紹了 Go 語言通過 error 類型統(tǒng)一進(jìn)行錯(cuò)誤處理,但這些錯(cuò)誤都是我們?cè)诰帉懘a時(shí)就已經(jīng)預(yù)見并返回的,對(duì)于某些運(yùn)行時(shí)錯(cuò)誤,比如數(shù)組越界、除數(shù)為0、空指針引用,這些 Go 語言是怎么處理的呢?

          panic

          Go 語言沒有像 Java、PHP 那樣引入異常的概念,也沒有提供 try...catch 這樣的語法對(duì)運(yùn)行時(shí)異常進(jìn)行捕獲和處理,當(dāng)代碼運(yùn)行時(shí)出錯(cuò),而又沒有在編碼時(shí)顯式返回錯(cuò)誤時(shí),Go 語言會(huì)拋出 panic,中文譯作「運(yùn)行時(shí)恐慌」,我們也可以將其看作 Go 語言版的異常。

          除了像上篇教程演示的那樣由 Go 語言底層拋出 panic,我們還可以在代碼中顯式拋出 panic,以便對(duì)錯(cuò)誤和異常信息進(jìn)行自定義,仍然以上篇教程除數(shù)為 0 的示例代碼為例,我們可以這樣顯式返回 panic 中斷代碼執(zhí)行:

          package main

          import "fmt"

          func main() {
              defer func() {
                  fmt.Println("代碼清理邏輯")
              }()

              var i = 1
              var j = 0
              if j == 0 {
                  panic("除數(shù)不能為0!")
              }
              k := i / j
              fmt.Printf("%d / %d = %d\n", i, j, k)
          }

          這樣一來,當(dāng)我們執(zhí)行這段代碼時(shí),就會(huì)拋出 panic:

          panic 函數(shù)支持的參數(shù)類型是 interface{}

          func panic(v interface{})

          所以可以傳入任意類型的參數(shù):

          panic(500)   // 傳入數(shù)字
          panic(errors.New("除數(shù)不能為0"))  // 傳入 error 類型

          無論是 Go 語言底層拋出 panic,還是我們?cè)诖a中顯式拋出 panic,處理機(jī)制都是一樣的:當(dāng)遇到 panic 時(shí),Go 語言會(huì)中斷當(dāng)前協(xié)程(即 main 函數(shù))后續(xù)代碼的執(zhí)行,然后執(zhí)行在中斷代碼之前定義的 defer 語句(按照先入后出的順序),最后程序退出并輸出 panic 錯(cuò)誤信息,以及出現(xiàn)錯(cuò)誤的堆棧跟蹤信息,也就是下面紅框中的內(nèi)容:

          第一行表示出問題的協(xié)程,第二行是問題代碼所在的包和函數(shù),第三行是問題代碼的具體位置,最后一行則是程序的退出狀態(tài),通過這些信息,可以幫助你快速定位問題并予以解決。

          recover

          此外,我們還可以通過 recover() 函數(shù)對(duì) panic 進(jìn)行捕獲和處理,從而避免程序崩潰然后直接退出,而是繼續(xù)可以執(zhí)行后續(xù)代碼,實(shí)現(xiàn)類似 Java、PHP 中 try...catch 語句的功能。

          由于執(zhí)行到拋出 panic 的問題代碼時(shí),會(huì)中斷后續(xù)其他代碼的執(zhí)行,所以,顯然這個(gè) panic 的捕獲應(yīng)該放到 defer 語句中完成,才可以在拋出 panic 時(shí)通過 recover 函數(shù)將其捕獲,defer 語句執(zhí)行完畢后,會(huì)退出拋出 panic 的當(dāng)前函數(shù),回調(diào)調(diào)用它的地方繼續(xù)后續(xù)代碼的執(zhí)行。

          可以類比為 panic、recover、defer 組合起來實(shí)現(xiàn)了傳統(tǒng)面向?qū)ο缶幊坍惓L幚淼?try...catch...finally 功能。

          下面我們引入 recover() 函數(shù)來重構(gòu)上述示例代碼如下:

          package main

          import (
              "fmt"
          )

          func divide() {
              defer func() {
                  if err := recover(); err != nil {
                      fmt.Printf("Runtime panic caught: %v\n", err)
                  }
              }()

              var i = 1
              var j = 0
              k := i / j
              fmt.Printf("%d / %d = %d\n", i, j, k)
          }

          func main() {
              divide()
              fmt.Println("divide 方法調(diào)用完畢,回到 main 函數(shù)")
          }

          如果沒有通過 recover() 函數(shù)捕獲 panic 的話,程序會(huì)直接崩潰退出,并打印錯(cuò)誤和堆棧信息:

          而現(xiàn)在我們?cè)?divide() 函數(shù)的 defer 語句中通過 recover() 函數(shù)捕獲了 panic,并打印捕獲到的錯(cuò)誤信息,這個(gè)時(shí)候,程序會(huì)退出 divide() 函數(shù)而不是整個(gè)應(yīng)用,繼續(xù)執(zhí)行 main() 函數(shù)中的后續(xù)代碼,即恢復(fù)后續(xù)其他代碼的執(zhí)行:

          如果在代碼執(zhí)行過程中沒有拋出 panic,比如我們把 divide() 函數(shù)中的 j 值改為 1,則代碼會(huì)正常執(zhí)行到函數(shù)末尾,然后調(diào)用 defer 語句聲明的匿名函數(shù),此時(shí) recover() 函數(shù)返回值為 nil,不會(huì)執(zhí)行 if 分支代碼,然后退出 divide() 函數(shù)回到 main() 函數(shù)執(zhí)行后續(xù)代碼:

          這樣一來,當(dāng)程序運(yùn)行過程中拋出 panic 時(shí)我們可以通過 recover() 函數(shù)對(duì)其進(jìn)行捕獲和處理,如果沒有拋出則什么也不做,從而確保了代碼的健壯性。

          以上就是 Go 語言錯(cuò)誤和異常處理的全部語法,非常簡單明了。接下來,我們將基于目前已經(jīng)學(xué)習(xí)的基礎(chǔ)語法對(duì) Go 語言編程進(jìn)行優(yōu)化和增強(qiáng) —— 介紹如何通過 Go 代碼實(shí)現(xiàn)常見的數(shù)據(jù)結(jié)構(gòu)和算法,以及如何在 Go 語言中實(shí)現(xiàn)常見的設(shè)計(jì)模式。

          (本文完)


          學(xué)習(xí)過程中有任何問題,可以通過下面的評(píng)論功能或加入「Go 語言研習(xí)社」與學(xué)院君討論:


          本系列教程首發(fā)在 geekr.dev,你可以點(diǎn)擊頁面左下角閱讀原文鏈接查看最新更新的教程。

          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  殴美日韩中文在线中 | 日韩欧美在线免费 | 成年人看的毛片 | 久久激情视频 | 北条麻妃九九九在线视频 |