二、Exception和Error有什麼區別

    世界上存在永遠不出錯的程序嗎?也許這隻會出現在程序員的夢中。(我覺得我寫的程序沒問題,都是他們使用的方式不對,哈哈哈)隨着編程語言和軟件的誕生,異常情況就如影隨行地糾纏着我們,只有正確處理好意外情況,才能保證程序地可靠性。

    Java語言在設計之初就提供了相對完善的異常處理機制,這也是Java得以大行其道的原因之一,因爲這種機制大大降低了編寫和維護可靠性程序的門檻。如今,異常處理機制以及成爲現代編程語言的標配。

    今天我要問你的問題是:請對比Exception和Error,另外,運行時異常與一般異常有什麼區別?

    典型回答

    Exception和Error都是繼承了Throwable類,在Java中只有Throwable類型的實例纔可以被拋出(throw)或者捕捉(catch),它是異常處理機制的基本組成類型。

    Error是指在正常情況下,不大可能出現的情況,絕大部分Error都會導致程序(比如JVM自身)處於非正常的、不可恢復狀態。既然是非正常情況,所以不便於也不需要捕獲,常見的比如OutOfMemoryError之類,都是Error的子類。

    Excpetion又分爲可檢查(checked)異常和不檢查(unchecked)異常,可檢查異常在源代碼裏必須顯式的進行捕獲處理,這是編譯器檢查的一部分。前面我介紹的不可查的Error,是Throwable不是Exception。

    不檢查異常就是所謂的運行時異常,類似NullPointerException(空指針)、ArrayIndexOutOfBoundsException(數組下標越界)之類,通常是可以編碼避免的邏輯錯誤,具體根據需要來判斷是否需要捕獲,並不會在編譯器強制要求。

    我覺得Error是錯誤,無法恢復了,就是那種涼透了(StackOverflow等)。Exception還能挽救,包括編譯時異常,運行時異常,都還有得救。

    知識擴展

    前面談的大多是概念性的東西,下面我來談些實踐中的選擇,我會結合代碼用例進行分析。

    這段代碼雖然很短,但是以及違反了異常處理的兩個基本原則。

    第一,儘量不要捕獲類似Exception這樣的通用異常(之前我覺得很省事,一直是這麼幹的),而是應該捕獲特定異常,在這裏是Thread.sleep()拋出的InterruptedException。

    這是因爲在日常的開發和合作中,我們讀代碼的機會往往超過寫代碼(我這是寫代碼寫的多,公司一個項目一個項目的接,難搞噢),軟件工程是門協作的藝術,所以我們有義務讓自己的代碼能夠直觀地實現出儘量多的信息,而泛泛的Exception之類,恰恰隱藏了我們的目的。另外,我們也要保證程序不被捕獲到我們不希望捕獲的異常。比如,你可能更希望RuntimeException被擴展出來,而不是捕獲。

    第二,不要生吞(swallow)異常。這是異常處理中要特別注意的事情,因爲很可能會導致非常難以診斷的詭異情況。

    生吞異常,往往是基於假設這段代碼可能不會發生,或者感覺忽略異常是無所謂的,但是千萬不要在產品代碼做這種假設!

    如果我們不把異常拋出來,或者也沒有輸出到日誌(Logger)之類,程序可能在後續代碼以不可控方式結束。沒人能夠輕易判斷究竟是哪裏拋出了異常,以及是什麼原因產生了異常。(日誌能必須寫清楚,也要規範噢)

     第二段代碼:

    這段代碼作爲一段實現代碼,他是沒有任何問題的,但是在產品代碼中,通常都不允許這樣處理。你先思考一下這是爲什麼呢?

    我們先來看看printStackTrace()的文檔,開頭就是“Prints this throwable and its backtrace to the standard error stream”。問題就在這裏,在稍微複雜一點的生成環境中,標準出錯(STERR)不是個合適的輸出選項,因爲你很難判斷出到底輸出到哪裏去了。

    尤其是對於分佈式系統,如果發生異常,但是無法找到堆棧軌跡(stacktrace),這純屬是爲了診斷設置障礙。所以,最好使用產品日誌,詳細的輸出到日誌系統裏。(log4j/backlog等)

    本文章來自於《極客時間APP》楊曉峯老師的課程《Java核心技術36講》,謝謝老師。

    其他回答

    在Java世界裏,異常的出現讓我們編寫的程序運  行起來更加的健壯,同時爲程序在調試、運行期  間發生的一些意外情況,提供了補救機會;即使  遇到一些嚴重錯誤而無法彌補,異常也會非常忠實的記錄所發生的這一切。以下是文章心得感悟  1不要推諉或延遲處理異常,就地解決最好,並且  需要實實在在的進行處理,而不是隻捕捉,不動作。  2—個函數儘管拋出了多個異常,但是隻有一個異  常可被傳播到調用端。最後被拋出的異常時唯一被調用端接收的異常,其他異常都會被吞沒掩蓋。如果調用端要知道造成失敗的最初原因,程序之中就絕不能掩蓋任何異常。  3不要在finally代碼塊中處理返回值(PS:也不要在finally中處理業務代碼,我之前犯過,被老大說了很久哈哈哈)。  4按照我們程序員的慣性認知:當遇到return語句  的時候,執行函數會立刻返回。但是,在Java語  言中,如果存在finally就會有例外。除了return語  句,try代碼塊中的break或continue語句也可能  使控制權進入finally代碼塊。  5請勿在try代碼塊中調用return、break或  continue語句。萬一無法避免,一定要確保finally  的存在不會改變函數的返回值。6函數返回值有兩種類型:值類型與對象引用。對於對象引用,要特別小心,如果在finally代碼塊中對函數返回的對象成員屬性進行了修改,即使不在finally塊中顯式調用return語句,這個修改也會作用於返回值上。7勿將異常用於控制流。8如無必要,勿用異常。

    越努力,越幸運,大家一起加油。⛽️

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章