【Golang筆記03】error、panic、fatal錯誤處理學習筆記

Golang筆記:錯誤處理學習筆記

一、進階學習

1.1、錯誤(異常處理)

Go語言中也有和Java中的異常處理相關的機制,不過,在Go里面不叫異常,而是叫做:錯誤。錯誤分為三類,分別是:

  • error:部分流程錯誤,需要程序員進行處理,這種級別的錯誤,不會導致整個程序停止運行。
  • panic:嚴重錯誤,等程序執行完成之后,會立即退出運行。
  • fatal:致命錯誤,整個程序立即停止運行。

Go語言中,不存在try..catch相關的語句,Go中的錯誤是通過返回值的形式返回的,例如:在調用一個方法的時候,這個方法可以增加一個返回值,表示是否存在錯誤信息,然后通過if條件語句判斷,是否能夠正常執行后續代碼。

上面這種錯誤的處理方式,雖然簡化了try...catch的使用,但是,引入了if條件語句,也會導致代碼里面出現一大堆的條件判斷,所以,從本質上來說,還是沒有簡化的,只不過是采用另一種方式處理錯誤而已。

1.1.1、error錯誤

Go中有一個error接口,接口里面只有一個Error()方法,返回值是一個string字符串,這個字符串表示錯誤信息。Go中創建error錯誤,可以通過下面兩種方式:

  • 第一種方式:使用errors包下的New()函數。
  • 第二種方式:使用fmt包下的Errorf()函數。
package mainimport ("errors""fmt"
)// 返回值第二個參數是 error 錯誤
func div(a int, b int) (int, error) {if b == 0 {// 定義 error 級別的錯誤返回return -1, errors.New("分母不能為0")// 或者使用下面這種方式// return -1, fmt.Errorf("分母不能為0")}c := a / breturn c, nil
}func main() {i, err := div(1, 0)if err != nil {fmt.Println(err)return}fmt.Println("結果=", i)
}
1.1.2、自定義error錯誤

Go中也允許程序開發人員自定義error級別的錯誤。自定義錯誤很簡單,只需要對應的類型,實現了Error()方法,那么這個類型就將被看作是error接口的實現類,也就屬于error類型的錯誤了。

自定義error錯誤的步驟,如下所示:

  • 第一步:自定義一個類型。
  • 第二步:實現Error()方法,返回string錯誤字符串信息。
  • 第三步:創建自定義類型對象,作為error錯誤即可。

案例代碼,如下所示:

package mainimport ("fmt"
)// 第一步:MyCustomError 自定義錯誤類型
type MyCustomError struct {code intmsg  string
}// 第二步:實現 error 接口的 Error() 方法
func (e MyCustomError) Error() string {// 自定義錯誤的返回格式return fmt.Sprintf("自定義錯誤:code=%d, msg=%s", e.code, e.msg)
}func main() {// 第三步:創建自定義錯誤customError := MyCustomError{code: 1,msg:  "分母不能為0",}fmt.Println(customError)
}
1.1.3、Unwrap解包錯誤

Go語言中,允許錯誤嵌套,一個錯誤嵌套一個錯誤,從而形成一個錯誤鏈表。要想從一個錯誤中,獲取它包含的錯誤,可以調用errors中提供的Unwrap()方法。

  • Unwrap()方法:從當前error錯誤中,獲取其內部包含的error錯誤,如果內部不存在error錯誤,則返回nil;如果存在,則返回內部的error錯誤。

需要注意的是,Unwrap()方法獲取到的error錯誤,有可能也是嵌套的error錯誤,如果想要獲取指定的error錯誤,可以使用遞歸的方式進行判斷。

package mainimport ("errors""fmt"
)// MyCustomError 自定義錯誤類型
type MyCustomError struct {code intmsg  string
}// 實現 error 接口的 Error() 方法
func (e MyCustomError) Error() string {// 自定義錯誤的返回格式return fmt.Sprintf("自定義錯誤:code=%d, msg=%s", e.code, e.msg)
}func main() {// 創建自定義錯誤customError := MyCustomError{code: 1,msg:  "分母不能為0",}// 嵌套錯誤,形成一個錯誤鏈err := fmt.Errorf("錯誤:%w", customError)fmt.Println(err)// 解包錯誤err = errors.Unwrap(err)fmt.Println(err)// 解包錯誤err = errors.Unwrap(err)fmt.Println(err)
}
// 執行結果
錯誤:自定義錯誤:code=1, msg=分母不能為0
自定義錯誤:code=1, msg=分母不能為0
<nil>

從上面可以看到,errors.Unwrap()方法的作用就是從錯誤鏈中解包內部錯誤。

1.1.4、檢查錯誤

Go語言中,判斷某個error錯誤是否為指定類型,可以使用errors.Is()方法,這個方法的作用是:判斷當前error是否為目標target類型的錯誤,如果是目標類型的錯誤,則返回true,否則返回false

  • errors.Is(err, targer error) bool方法:判斷是否為目標錯誤類型。
package mainimport ("errors""fmt"
)var originalErr = errors.New("this is an error")// 包裹原始錯誤
func wrap1() error {return fmt.Errorf("wrapp error %w", wrap2())
}// 原始錯誤
func wrap2() error {return originalErr
}func main() {err := wrap1()// 如果使用if err == originalErr 將會是falseif errors.Is(err, originalErr) {fmt.Println("original")}
}

另外,Go中還提供了一個errors.As(err, target Any) bool方法,這個方法的作用是:在當前錯誤鏈中尋找第一個匹配target的錯誤類型,并且將找到的錯誤賦值給傳入的err變量。

  • errors.As(err, target Any) bool方法:用于檢查一個錯誤是否可以轉換成目標,如果可以就會將目標錯誤賦值給第一個參數err
package mainimport ("errors""fmt""time"
)// TimeError 自定義error
type TimeError struct {Msg string// 記錄發生錯誤的時間Time time.Time
}// 實現 error 接口方法
func (m TimeError) Error() string {return m.Msg
}// NewMyError 定義自定義錯誤函數,并且使用 & 符號,返回一個 結構體指針 類型
func NewMyError(msg string) error {// 使用 & 符號,返回一個 結構體指針 類型,即:返回內存地址return &TimeError{Msg:  msg,Time: time.Now(),}
}// 包裹原始錯誤
func wrap1() error {return fmt.Errorf("wrapp error %w", wrap2())
}// 原始錯誤
func wrap2() error {return NewMyError("original error")
}func main() {// 定義一個 結構體指針 變量var myerr *TimeError// 獲取錯誤err := wrap1()// 檢查錯誤鏈中是否有 *TimeError 類型的錯誤if errors.As(err, &myerr) { // 輸出TimeError的時間fmt.Println("original", myerr.Time)}
}

1.2、panic

前面學的error錯誤,是不嚴重的錯誤類型,如果不在后面寫return語句,那么程序還是會繼續往后執行下去的。Go語言中還提供了一個panic錯誤。

panic類型是一種比error更加嚴重的錯誤類型,當出現panic之后,后續的代碼時不會繼續執行,即使沒有使用return關鍵字,代碼也不會執行panic后面的代碼。

注意:panic你可以理解成是Java語言中的使用throw new拋出異常的模式。

1.2.1、創建panic

Go中提供了一個panic函數,通過這個函數可以創建panic錯誤。

func panic(v any)

我對panic的理解是,panic就相當于是簡寫了Java語言中的throw new Exception("錯誤信息")的語句。例如:

// 在Java語言中,拋出異常
throw new Exception("錯誤異常信息")// 而在Go里面,只需要寫一個 panic 函數即可
panic("錯誤異常信息")

使用panic的時候,需要注意的是,panic后面的代碼是不會執行的。

1.2.2、panic善后

當發生panic錯誤之前,如果存在defer延遲函數,那么程序首先會依次執行defer延遲函數,執行完成之后,才會觸發panic

package mainimport "fmt"func mockPanic() {fmt.Println("開始執行panic...")panic("模擬panic錯誤...")fmt.Println("panic執行結束...")
}
func demo01() {fmt.Println("執行demo01()函數...")
}
func demo02() {fmt.Println("執行demo02()函數...")
}
func demo03() {fmt.Println("執行demo03()函數...")
}func main() {fmt.Println("Hello")defer demo01()defer demo02()mockPanic()defer demo03()fmt.Println("World")
}// 程序運行結果
Hello
開始執行panic...
執行demo02()函數...
執行demo01()函數...
panic: 模擬panic錯誤...goroutine 1 [running]:
main.mockPanic()D:/environment/GoWorks/src/go-study/Hello.go:7 +0x59
main.main()D:/environment/GoWorks/src/go-study/Hello.go:24 +0x7d

當發生panic時,會立即停止當前函數的執行,然后執行善后的一些操作,例如:執行defer延遲函數;執行完成之后,會將panic向上層函數傳遞,上層函數也會進行相應的善后操作,直到main函數終止運行。

1.2.3、恢復執行

當使用panic之后,程序會終止運行,如果我們想恢復程序的執行,那么可以使用recover()內置函數來實現。需要注意的是,recover()函數必須在defer延遲函數里面使用。另外,recover()函數的返回值是panic函數中的錯誤信息內容。

package mainimport ("fmt"
)func demo() {fmt.Println("C")defer func() {fmt.Println("D")// 恢復程序執行,就相當于是取消panicok := recover()if ok != nil {fmt.Println("恢復程序執行")}fmt.Println("E")}()// 發生 panic 則 demo 方法終止執行panic("模擬panic錯誤...")fmt.Println("F")
}func main() {fmt.Println("A")demo()fmt.Println("B")
}// 執行結果
A
C
D
恢復程序執行
E
B

從上面代碼中,可以發現,正常情況下,發生panic之后,如果沒有使用recover()函數恢復執行,那么最終的輸出結果將只有下面這些內容:

A
C
D
E
panic: 模擬panic錯誤...goroutine 1 [running]:
main.demo()D:/environment/GoWorks/src/go-study/Hello.go:19 +0x76
main.main()D:/environment/GoWorks/src/go-study/Hello.go:25 +0x4f

但是,我們在demo()函數中,定義了一個defer延遲函數,并且在里面使用recover()函數恢復程序的執行,也就相當于是捕獲處理了panic,那么demo()函數中的panic就不會向上傳遞到main函數里面,所以main函數中的代碼將正常執行。

理解這一點很重要,因為如果把上面代碼改成下面執行順序,如下所示:

package mainimport ("fmt"
)func demo() {fmt.Println("C")// 發生 panic 則 demo 方法終止執行panic("模擬panic錯誤...")fmt.Println("F")
}func main() {fmt.Println("A")// 在main函數里面定義deferdefer func() {fmt.Println("D")// 恢復程序執行,就相當于是取消panicok := recover()if ok != nil {fmt.Println("恢復程序執行")}fmt.Println("E")}()demo()fmt.Println("B")
}// 執行結果
A
C
D
恢復程序執行
E

上面代碼中,從輸出結果來看,程序沒有執行demo()函數之后的代碼,這是為什么呢???

  • 我是這么理解的,當demo()函數中,發生panic之后,由于demo()函數沒有進行處理,所以會向上傳遞到main()函數里面。
  • main()函數發現,此時發生了panic,所以就不會執行后面的代碼。
  • 此時觸發panic的善后相關代碼,例如:執行defer函數。
  • 首先,會從demo()函數中,依次開始執行defer函數,然后向上傳遞到main函數里面,開始執行main函數中的defer函數。
  • defer函數執行完成之后,此時整個程序就結束運行了。
  • 所以,最終demo()函數之后的代碼,也就不會再執行了。

下面舉個例子,來看看發生panic之后,程序的輸出結果分別是多少,如下所示:

package mainimport ("fmt"
)func demo() {fmt.Println("B1")defer fmt.Println("C")defer func() {fmt.Println("E1")// 恢復程序執行,就相當于是取消panicok := recover()if ok != nil {fmt.Println("恢復程序執行")}fmt.Println("E2")}()demo02()fmt.Println("B2")
}func demo02() {fmt.Println("D1")panic("demo02函數發生panic...")fmt.Println("D2")
}func main() {fmt.Println("A1")demo()fmt.Println("A2")
}// 輸出結果
// A1 B1 D1 E1 恢復... E2 C A2

看到這里,你學會了嗎???

使用recover()函數,有四個注意事項:

  • 必須在defer函數中使用recover()函數。
  • 多次使用recover()函數,只會恢復一個panic
  • 閉包結構中使用recover()函數,不能恢復外部函數的panic
  • panic參數禁止使用nil

可以將recover()函數理解成是Java中的try...catch的功能,也就是捕獲panic錯誤,然后能夠讓程序繼續正常執行。

1.3、fatal

fatalGo語言中的致命錯誤,這種錯誤一旦發生,那么整個程序就會立即停止運行,fatal是沒有辦法進行善后操作的,也就是說,發生fatal之后,程序都來不及執行defer函數。

package mainimport ("fmt""os"
)func main() {fmt.Println("A1")// 模擬 fatal 錯誤os.Exit(1)fmt.Println("A2")// A1 B1 D1 E1 恢復 E2 C A2
}

Go語言中一般不會顯示的聲明fatal錯誤,發生fatal錯誤一般都是程序自主發生的,不是人為干預的。

注意:Go語言中一般使用os.Exit(1)代碼來實現fatal錯誤。

以上,就是Go語言中錯誤處理相關的知識點。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/84095.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/84095.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/84095.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python可視化設計原則

在數據驅動的時代&#xff0c;可視化不僅是結果的呈現方式&#xff0c;更是數據故事的核心載體。Python憑借其豐富的生態庫&#xff08;Matplotlib/Seaborn/Plotly等&#xff09;&#xff0c;已成為數據可視化領域的主力工具。但工具只是起點&#xff0c;真正讓圖表產生價值的&…

??WPF入門與XAML基礎:從零開始構建你的第一個WPF應用?

從零開始構建你的第一個WPF應用? 1.什么是WPF&#xff1f;??2.開發環境搭建??2.1 安裝Visual Studio??2.2 創建第一個WPF項目?? 3. WPF項目結構解析????3.1 MainWindow.xaml??3.2 MainWindow.xaml.cs?? 4. XAML基礎語法??4.1 屬性賦值方式??4.2 命名空間&…

電子電氣架構 --- 下一代汽車電子電氣架構中的連接性

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 鈍感力的“鈍”,不是木訥、遲鈍,而是直面困境的韌勁和耐力,是面對外界噪音的通透淡然。 生活中有兩種人,一種人格外在意別人的眼光;另一種人無論…

學習日記-day13-5.22

完成目標&#xff1a; 知識點&#xff1a; 1.抽象注意事項 知識點 核心內容 重點 抽象類實例化限制 抽象類不能直接實例化對象&#xff0c;只能創建非抽象子類的對象 嘗試實例化抽象類會觸發編譯錯誤 抽象方法與抽象類關系 抽象類不一定包含抽象方法&#xff0c;但含…

華碩無畏Pro14 2025,打造舒適辦公新體驗

在快節奏、高效率的現代辦公環境中&#xff0c;一臺得心應手的筆記本無疑是每位職場人士的“第二大腦”與核心生產力工具。它不僅需要承載日常工作的繁雜事務&#xff0c;更要在關鍵時刻穩定輸出&#xff0c;助力我們從容應對各種挑戰。 洞悉此需求&#xff0c;華碩推出了全新…

重寫B站(網頁、后端、小程序)

1. 網頁端 1.1 框架 Vue ElementUI axios 1.2 框架搭建步驟 搭建Vue 1.3 配置文件 main.js import {createApp} from vue import ElementUi from element-plus import element-plus/dist/index.css; import axios from "axios"; import router from…

MySQL數據 在 磁盤上是什么樣子的

MySQL數據 在 磁盤上是什么樣子的&#xff0c;取決于所使用的存儲引擎。存儲于引擎 是作用在 表! 上的。 存儲引擎 百度百科是這樣定義存儲引擎的&#xff1a;MySQL 中的數據用各種不同的技術存儲在文件&#xff08;或者內存&#xff09;中&#xff0c;這些不同的技術以及配套…

MySQL的相關操作

目錄 一. 字符串函數 二. group by分組 2.1 作用 2.2 格式 2.3 舉例 三. order by排序 3.1 格式 3.2 舉例 四. limit 4.1 作用 4.2 舉例 五. having 5.1 作用 5.2 舉例 六. 正則表達式 七. 多表查詢 7.1 定義 7.2 子查詢 7.3 聯合查詢 縱向合并 7.4 交叉連…

網絡安全-等級保護(等保) 2-7-3 GB/T 25058—2019 第7章 安全設計與實施

############################################################################### 對于安全廠家而言&#xff0c;最關心的內容在本章節&#xff0c;根據已確定的安全總體方案&#xff0c;完成技術措施和管理措施的詳細設計和實施&#xff0c;包含具體的安全產品和管理要求。…

【Spring Boot】配置實戰指南:Properties與YML的深度對比與最佳實踐

目錄 1.前言 2.正文 2.1配置文件的格式 2.2properties 2.2.1基礎語法 2.2.2value讀取配置文件 2.2.3缺點 2.3yml 2.3.1基礎語法 2.3.2配置不同數據類型 2.3.3配置讀取 2.3.4配置對象和集合 2.3.5優缺點 2.4綜合練習&#xff1a;驗證碼案例 2.4.1分析需求 2.4.2…

20250519使用TF卡將NanoPi NEO core開發板刷機為Ubuntu core22.04.3系統完成之后執行poweroff自動關機

1、h3-sd-friendlycore-xenial-4.14-armhf-20210618.img.gz 在WIN10下使用7-ZIP解壓縮/ubuntu20.04下使用tar 2、Win32DiskImager.exe 寫如32GB的TF卡。【以管理員身份運行】 3、TF卡如果已經做過會有3個磁盤分區&#xff0c;可以使用SD Card Formatter/SDCardFormatterv5_WinE…

編譯Qt5.15.16并啟用pdf模塊

編譯Qt5.15.16并啟用pdf模塊 標題1.目錄設置 -q-bulid –qt-everywhere-src-5.15.16 –bulid cd bulid 必須&#xff0c;否則會提示Project ERROR: You cannot configure qt separately within a top-level build. create .qmake.stash and .qmake.super in build folder …

“智”斗秸稈焚燒,考拉悠然以科技之力筑牢生態安全防線

清晨&#xff0c;薄霧籠罩著遼闊的田野&#xff0c;農民們開始了一天的勞作。然而&#xff0c;隨著收割季的到來&#xff0c;秸稈焚燒問題也逐漸浮現&#xff0c;成為威脅空氣質量與生態安全的隱患。傳統監管方式往往顯得力不從心&#xff0c;效率低下的困境亟待突破。在此背景…

Nockchain項目部署教程

Nockchain頭礦窗口正在打開&#xff0c;不拼設備&#xff0c;現在部署&#xff0c;馬上就要開挖了。 一、項目介紹 Nockchain 是結合了POW和ZKVM的區塊鏈協議。 主要特點&#xff1a; 1&#xff09;計算存儲新域名空間三位一體架構&#xff0c;高吞吐量 2&#xff09;使用No…

2025年氣候持續大風,消納減少,如何保收益?東潤能源整體解決方案持續保收益保增長

引言 隨著全球氣候變化加劇,2025年極端天氣頻發,風資源豐富但電網消納能力不足的問題日益突出,導致許多風電項目面臨限電、收益下滑的挑戰。如何在復雜的氣候和電力市場環境下保障投資收益,成為行業關注的焦點。東潤能源憑借領先的技術創新和綜合能源解決方案,為風電行業…

2023河南CCPC省賽vp部分補題

A 模擬 暴力 對每個合法的前綴&#xff0c;判斷后綴是不是合法 int a[29]; void solve(){string s;cin>>s;int t-1;if(s.size()1){return cout<<"NaN"<<endl,void();}for(int i0;i<27;i) a[i]0;for(int i0;i<s.size();i){a[s[i]-a];if(…

【2025保姆級】Open-WebUI五大功能區首曝!第一篇:管理員面板深度拆解,手把手講解配置AI管理中樞

【2025保姆級】Open-WebUI五大功能區首曝&#xff01;第一篇&#xff1a;管理員面板深度拆解&#xff0c;手把手講解&配置AI管理中樞 一、引言二、用戶2.1 概述2.2 權限組 三、競技場評估四、函數五、設置5.1 通用5.1.1 身份驗證5.1.2 功能 5.2 外部連接5.2.1 OpenAI API5.…

docker上傳鏡像

向Docker Hub上傳鏡像&#xff0c;需要按照一定的步驟進行操作。 Docker Hub是Docker的官方鏡像倉庫&#xff0c;用戶可以在其中存儲、管理和部署Docker鏡像。要向Docker Hub上傳鏡像&#xff0c;請遵循以下步驟&#xff1a; 創建Docker Hub賬戶&#xff1a; 訪問Docker Hub官…

(十三)深入了解AVFoundation-采集:視頻幀采集與實時濾鏡處理

引言 在移動應用中&#xff0c;實時視頻處理已成為視頻拍攝、短視頻、直播、美顏相機等功能的核心技術之一。從簡單的濾鏡疊加&#xff0c;到復雜的美顏、AR 特效&#xff0c;背后都離不開對每一幀圖像的高效采集與處理。在前幾篇文章中&#xff0c;我們已經實現了基本的視頻采…

數字政務安全實戰:等保2.0框架下OA系統防護全解析

近期在Python基礎教學領域深入鉆研函數機制、數據結構優化等內容時&#xff0c;深刻意識到信息安全作為技術基石的戰略價值。在政務數字化轉型浪潮中&#xff0c;Python憑借其高擴展性與豐富的安全生態庫&#xff0c;成為構建政務OA系統安全防護體系的核心工具。本文將以等保2.…