網站首頁 編程語言 正文
根據代碼實例運行結果來總結
說明:定義一個函數,有多個defer (用于判斷多個defer執行順序),有panic和 return (判斷與defer對比執行順序)
一、函數中有panic
package main import "fmt" func main() { fmt.Println("main func start") defer func(){ fmt.Println("main defer func 1") }() s := test() fmt.Println("main get test() return:",s) } func test() (str string) { defer func() { //捕獲panic if msg := recover(); msg != nil { fmt.Println("test defer func1 捕獲到錯誤:",msg) } str = "bbb" }() defer func(){ fmt.Println("test defer func2") }() defer func(){ fmt.Println("test defer func3") }() str = "aaa" fmt.Println("panic拋出前") panic("test painc") fmt.Println("panic拋出后") return str }
執行結果:
根據執行結果可知道:
- 函數內多個defer執行順序是 先入后出(即入棧)
- panic 先于defer執行,不然defer函數內捕獲不到錯誤
- panic執行后 后續邏輯及return 沒有執行
二、然后將代碼中 panic注釋掉再執行
執行結果:
根據執行結果可知:
- defer中可以修改返回值,注意:前提是函數的返回值不是匿名的
三、函數返回的是匿名參數
package main import "fmt" func main() { fmt.Println("main func start") defer func(){ fmt.Println("main defer func 1") }() s := test() fmt.Println("main get test() return:",s) } func test() (string) { str := "aaa" defer func() { //捕獲panic if msg := recover(); msg != nil { fmt.Println("test defer func1 捕獲到錯誤:",msg) } str = "ccc" }() defer func(){ fmt.Println("test defer func2") }() defer func(){ fmt.Println("test defer func3") }() fmt.Println("panic拋出前") panic("test painc") fmt.Println("panic拋出后") return str }
執行結果:
然后注釋掉panic執行結果
根據執行結果:
- 函數返回參數是匿名的 defer無法修改
- 函數中有panic 匿名的返回值是零值,因為return賦值得不到執行,defer又修改不到返回值
***注意(非常重要):這里需要提到的是函數的return是分為兩個步驟:return最先執行,先將結果寫入返回值中(即賦值);接著defer開始執行一些收尾工作;最后函數攜帶當前返回值退出(即返回值)。
有panic的時候,return第一步沒有執行到,無法將結果寫入返回值中,那么函數退出前則只能返回參數類型的零值
四、總結:
- 函數中有多個defer,則是按先進后出(壓棧)執行
- panic先于defer執行,所以能通過defer中去捕獲panic錯誤
- defer可以修改函數的返回參數,前提是函數返回的參數不是匿名的
- 函數執行出現panic那么return得不到執行,如果返回參數是匿名的,那么函數最終返回的是返回參數的類型零值,如果返回參數不是匿名的,在panic前有對返回參數賦值,那么就能返回這個值,如果defer有對其修改,那么返回值則是defer修改的。
ps:go語言錯誤和異常處理,panic、defer、recover的執行順序
一、panic()和recover()
Golang中引入兩個內置函數panic和recover來觸發和終止異常處理流程,同時引入關鍵字defer來延遲執行defer后面的函數。?一直等到包含defer語句的函數執行完畢時,延遲函數(defer后的函數)才會被執行,而不管包含defer語句的函數是通過return的正常結束,還是由于panic導致的異常結束。你可以在一個函數中執行多條defer語句,它們的執行順序與聲明順序相反。?當程序運行時,如果遇到引用空指針、下標越界或顯式調用panic函數等情況,則先觸發panic函數的執行,然后調用延遲函數。調用者繼續傳遞panic,因此該過程一直在調用棧中重復發生:函數停止執行,調用延遲執行函數等。如果一路在延遲函數中沒有recover函數的調用,則會到達該協程的起點,該協程結束,然后終止其他所有協程,包括主協程(類似于C語言中的主線程,該協程ID為1)。
panic:?1、內建函數?2、假如函數F中書寫了panic語句,會終止其后要執行的代碼,在panic所在函數F內如果存在要執行的defer函數列表,按照defer的逆序執行?3、返回函數F的調用者G,在G中,調用函數F語句之后的代碼不會執行,假如函數G中存在要執行的defer函數列表,按照defer的逆序執行,這里的defer?有點類似?try-catch-finally?中的?finally?4、直到goroutine整個退出,并報告錯誤
recover:?1、內建函數?2、用來控制一個goroutine的panicking行為,捕獲panic,從而影響應用的行為?3、一般的調用建議?a).?在defer函數中,通過recever來終止一個gojroutine的panicking過程,從而恢復正常代碼的執行?b).?可以獲取通過panic傳遞的error
簡單來講:go中可以拋出一個panic的異常,然后在defer中通過recover捕獲這個異常,然后正常處理。
錯誤和異常從Golang機制上講,就是error和panic的區別。很多其他語言也一樣,比如C++/Java,沒有error但有errno,沒有panic但有throw。
Golang錯誤和異常是可以互相轉換的:
錯誤轉異常,比如程序邏輯上嘗試請求某個URL,最多嘗試三次,嘗試三次的過程中請求失敗是錯誤,嘗試完第三次還不成功的話,失敗就被提升為異常了。異常轉錯誤,比如panic觸發的異常被recover恢復后,將返回值中error類型的變量進行賦值,以便上層函數繼續走錯誤處理流程。
什么情況下用錯誤表達,什么情況下用異常表達,就得有一套規則,否則很容易出現一切皆錯誤或一切皆異常的情況。
以下給出異常處理的作用域(場景):
空指針引用下標越界除數為0不應該出現的分支,比如default輸入不應該引起函數錯誤
其他場景我們使用錯誤處理,這使得我們的函數接口很精煉。對于異常,我們可以選擇在一個合適的上游去recover,并打印堆棧信息,使得部署后的程序不會終止。
說明: Golang錯誤處理方式一直是很多人詬病的地方,有些人吐槽說一半的代碼都是"if err != nil { / 打印 && 錯誤處理 / }",嚴重影響正常的處理邏輯。當我們區分錯誤和異常,根據規則設計函數,就會大大提高可讀性和可維護性。
代碼演示:
package main import "fmt" func main() { /* panic:詞義"恐慌", recover:"恢復" go語言利用panic(),recover(),實現程序中的極特殊的異常的處理 panic(),讓當前的程序進入恐慌,中斷程序的執行 recover(),讓程序恢復,必須在defer函數中執行 */ defer func(){ if msg := recover();msg != nil{ fmt.Println(msg,"程序回復啦。。。") } }() funA() defer myprint("defer main:3.....") funB() defer myprint("defer main:4.....") fmt.Println("main..over。。。。") } func myprint(s string){ fmt.Println(s) } func funA(){ fmt.Println("我是一個函數funA()....") } func funB(){//外圍函數 fmt.Println("我是函數funB()...") defer myprint("defer funB():1.....") for i:= 1;i<=10;i++{ fmt.Println("i:",i) if i == 5{ //讓程序中斷 panic("funB函數,恐慌了") } }//當外圍函數的代碼中發生了運行恐慌,只有其中所有的已經defer的函數全部都執行完畢后,該運行恐慌才會真正被擴展至調用處。 defer myprint("defer funB():2.....") }
?運行結果:
我是一個函數funA()....
我是函數funB()...
i: 1
i: 2
i: 3
i: 4
i: 5
defer funB():1.....
defer main:3.....
funB函數,恐慌了 程序回復啦。。。
可見當外圍函數的代碼中發生了運行恐慌,只有其中所有的已經defer的函數全部都執行完畢后,該運行恐慌才會真正被擴展至調用處。
原文鏈接:https://blog.csdn.net/leo_jk/article/details/128607869
相關推薦
- 2023-04-02 vscode?ssh遠程連接服務器的思考淺析_相關技巧
- 2022-05-05 Python學習之流程控制與條件判斷總結_python
- 2022-02-04 mybatis @Select jdbctype 可選類型
- 2022-09-07 Android組件化、插件化詳細講解_Android
- 2022-06-01 Kubernetes(K8S)入門基礎內容介紹_云和虛擬化
- 2022-04-09 一起來了解python的if流程控制_python
- 2022-06-08 淺析C++可變參數模板的展開方式_C 語言
- 2024-01-27 Linux關于Centos IP靜態配置
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支