Go語言錯誤和異常處理機制

錯誤和異常的區(qū)別

  • 錯誤和異常是不同的概念
  • 錯誤指的是可能出現(xiàn)問題的地方出現(xiàn)了問題吆你,比如打開一個文件時失敗,這種情況在人們的意料之中 左冬;而異常指的是不應該出現(xiàn)問題的地方出現(xiàn)了問題筐摘,比如引用了空指針鸦泳,這種情況在人們的意料之外∷信危可見惠豺,錯誤是業(yè)務過程的一部分,而異常不是
  • 從Go語言機制上講风宁,錯誤和異常就是error和panic的區(qū)別
  • Go語言錯誤和異常是可以互相轉換的:
  1. 錯誤轉異常洁墙,如程序嘗試請求某個URL,最多嘗試三次戒财,嘗試三次的過程中請求失敗是錯誤热监,嘗試完第三次還不成功的話,失敗就被提升為異常了
  2. 異常轉錯誤饮寞,如panic觸發(fā)的異常被recover恢復后孝扛,將返回值中error類型的變量進行賦值列吼,以便上層函數(shù)繼續(xù)走錯誤處理流程

Go語言錯誤處理機制

error

  • Go 語言通過內(nèi)置的錯誤接口提供了非常簡單的錯誤處理機制
type error interface {
    Error() string
}
  • Go提供了兩種創(chuàng)建一個實現(xiàn)了error interface的類型的變量實例的方法:errors.New和fmt.Errorf
errors.New("your first error code")
fmt.Errorf("error value is %d\n", errcode)
  • 如果函數(shù)要返回錯誤,則在返回值類型列表中包含error苦始,使用errors.New 可返回一個錯誤信息
func Sqrt(f float64) (float64, error) {
    if f < 0 {
        return 0, errors.New("math: square root of negative number")
    }
}
//調(diào)用函數(shù)
result, err:= Sqrt(-1)
if err != nil {
   fmt.Println(err)
}

錯誤處理的正確姿勢

  • 失敗的原因只有一個寞钥,所以返回值的類型應該為bool,而不是error
  • 沒有失敗時陌选,不使用error
  • error應放在返回值類型列表的最后
  • 錯誤值統(tǒng)一定義理郑,而不是跟著感覺走
  • 參考C/C++的錯誤碼定義文件,在Go的每個包中增加一個錯誤對象定義文件咨油,如下所示:
var ERR_EOF = errors.New("EOF") 
var ERR_CLOSED_PIPE = errors.New("io: read/write on closed pipe") 
var ERR_NO_PROGRESS = errors.New("multiple Read calls return no data or error") 
var ERR_SHORT_BUFFER = errors.New("short buffer") 
var ERR_SHORT_WRITE = errors.New("short write") 
var ERR_UNEXPECTED_EOF = errors.New("unexpected EOF")
  • 錯誤逐層傳遞時您炉,層層都加日志
  • 當嘗試幾次可以避免失敗時,不要立即返回錯誤
  • 當上層函數(shù)不關心錯誤時役电,建議不返回error赚爵,打印日志即可
  • 當發(fā)生錯誤時,不忽略有用的返回值
  • error的string context中的內(nèi)容格式:頭母小寫法瑟,結尾不帶標點
  • 不要忽略函數(shù)或方法返回的錯誤碼冀膝,Check it
  • 可在defer中處理error

Go語言異常處理機制

  • Go語言追求簡潔優(yōu)雅,所以瓢谢,Go語言不支持傳統(tǒng)的 try…catch…finally 這種異常畸写,因為Go語言的設計者們認為,將異常與控制結構混在一起會很容易使得代碼變得混亂氓扛。導致開發(fā)者很容易濫用異常,甚至一個小小的錯誤都拋出一個異常论笔。不要用異常代替錯誤采郎,更不要用來控制流程
  • 在極個別的情況下,也就是說狂魔,遇到真正的異常的情況下(比如除數(shù)為0了)蒜埋。才使用Go中引入的異常處理函數(shù)
  • Go中引入兩個內(nèi)置函數(shù)panic和recover來觸發(fā)和終止異常處理流程,同時引入關鍵字defer來延遲執(zhí)行defer后面的函數(shù)最楷,用于處理異常
  • panic和recover函數(shù)在作用層面分別對等throw和catch語句整份,當然也存在不同之處,panic和recover函數(shù)適用于那些真正的異常(例如整數(shù)除0)籽孙,而throw catch finally機制常常被用來處理一些業(yè)務層面的自定義異常和錯誤
  • go語言中烈评,panic和recover要慎用

panic

  • Go使用panic函數(shù)觸發(fā)異常,它可以在任何地方引發(fā)
  • panic一般會導致程序掛掉(除非recover) 然后Go運行時會打印出調(diào)用棧
  • 即使函數(shù)執(zhí)行的時候panic了犯建,函數(shù)不往下走了讲冠,運行時并不是立刻向上傳遞panic,而是到defer那适瓦,等defer的東西都跑完了竿开,panic再向上傳遞
func abc() {
panic("我是abc,我要拋出一個異常了谱仪,等下defer會通過recover捕獲這個異常,然后正常處理否彩,使后續(xù)程序正常運行疯攒。")
fmt.Println("我是PHP里panic后面要打印出的內(nèi)容。")
}
  • abc中panic后的代碼將不會執(zhí)行

recover&defer

  • Go使用defer和recover處理和恢復異常
  • panic會在調(diào)用它的函數(shù)中向本層和它的所有上層逐級拋出列荔,若一直沒有recover將其捕獲敬尺,程序退出后會產(chǎn)生crash;若在某層defer語句中被recover捕獲肌毅,控制流程將進入到recover之后的語句中
  • 必須要先聲明defer筷转,否則不能捕獲到panic異常,也就是說要先注冊函數(shù),后面有異常了悬而,才可以調(diào)用
  • defer 有點類似 try-catch-finally 中的 finally
  • 可以在一個函數(shù)中執(zhí)行多條defer語句呜舒,它們的執(zhí)行順序與聲明順序相反(堆棧,先進后出)
  • recover只有在defer調(diào)用的函數(shù)中有效
  • 選擇在一個合適的上游去recover笨奠,并打印堆棧信息袭蝗,使得部署后的程序不會終止
func abc() {
defer func() {
if err := recover(); err != nil {
fmt.Println("終于捕獲到了panic產(chǎn)生的異常:", err) // 這里的err就是panic傳入的內(nèi)容
fmt.Println("我是defer里的匿名函數(shù),我捕獲到panic的異常了般婆,我要recover恢復過來          了到腥。")
}
}() //注意這個()就是調(diào)用該匿名函數(shù)的
panic("我是abc,我要拋出一個異常了,等下defer會通過recover捕獲這個異常蔚袍,捕獲到我時乡范,在abc里是不會輸出的,會在defer里被捕獲輸出啤咽,然后正常處理晋辆,使后續(xù)程序正常運行")
fmt.Println("我是panic后面要打印出的內(nèi)容。但是我是永遠也打印不出來了宇整。因為邏輯并不會恢復到panic那個點去瓶佳,函數(shù)還是會在defer之后返回,也就是說執(zhí)行到defer后鳞青,程序直接返回到main()里霸饲,接下來開始執(zhí)行cba()")
} 

func cba() {
fmt.Println("我是cba,如果沒有defer來recover捕獲panic的異常臂拓,我是不會被正常執(zhí)行的厚脉。")
}

異常處理的正確姿勢

  • 在程序開發(fā)階段,調(diào)用panic函數(shù)來中斷程序的執(zhí)行以強制發(fā)生異常埃儿,使得該異常不會被忽略器仗,因而能夠盡快被修復
  • 在程序部署后,應恢復異常避免程序終止
  • 在調(diào)用recover的延遲函數(shù)中以最合理的方式響應該異常:
    打印堆棧的異常調(diào)用信息和關鍵的業(yè)務信息,以便這些問題保留可見
    將異常轉換為錯誤精钮,以便調(diào)用者讓程序恢復到健康狀態(tài)并繼續(xù)安全運行
  • 對于不應該出現(xiàn)的分支威鹿,使用異常處理
  • 針對入?yún)⒉粦撚袉栴}的函數(shù),使用panic設計
  • 異常處理的作用域(場景):
    空指針引用
    下標越界
    除數(shù)為0
    不應該出現(xiàn)的分支轨香,比如default
    輸入不應該引起函數(shù)錯誤
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末忽你,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子臂容,更是在濱河造成了極大的恐慌科雳,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脓杉,死亡現(xiàn)場離奇詭異糟秘,居然都是意外死亡,警方通過查閱死者的電腦和手機球散,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門尿赚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蕉堰,你說我怎么就攤上這事凌净。” “怎么了屋讶?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵冰寻,是天一觀的道長。 經(jīng)常有香客問我皿渗,道長斩芭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任乐疆,我火速辦了婚禮秒旋,結果婚禮上,老公的妹妹穿的比我還像新娘诀拭。我一直安慰自己,他們只是感情好煤蚌,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布耕挨。 她就那樣靜靜地躺著,像睡著了一般尉桩。 火紅的嫁衣襯著肌膚如雪筒占。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天蜘犁,我揣著相機與錄音翰苫,去河邊找鬼。 笑死,一個胖子當著我的面吹牛奏窑,可吹牛的內(nèi)容都是我干的导披。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼埃唯,長吁一口氣:“原來是場噩夢啊……” “哼撩匕!你這毒婦竟也來了?” 一聲冷哼從身側響起墨叛,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤止毕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后漠趁,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扁凛,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年闯传,在試婚紗的時候發(fā)現(xiàn)自己被綠了谨朝。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡丸边,死狀恐怖叠必,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情妹窖,我是刑警寧澤纬朝,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站骄呼,受9級特大地震影響共苛,放射性物質發(fā)生泄漏。R本人自食惡果不足惜蜓萄,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一隅茎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嫉沽,春花似錦辟犀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至玻佩,卻和暖如春出嘹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咬崔。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工税稼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留烦秩,地道東北人。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓郎仆,卻偏偏與公主長得像只祠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子丸升,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容