① Exception 和Error 都是繼承了Throwable類,在Java中只有Throwable類型的實例才可以被拋出或者捕獲,它是異常處理機制的基本類型。
② Exception和Error體現了Java平台設計者對不同異常情況的分類。
⑴Exception是程序正常運行中,可以預料的意外情況,可能並且應該被捕獲,進行相應處理。
⑵Exception又分為可檢查(checked)異常和不可檢查(unchecked)異常。可檢查異常在源代碼里必須顯式的進行捕獲處理,這是編譯期檢查的一部分。不可檢查時異常是指運行時異常,像NullPointerException、ArrayIndexOutOfBoundsException之類,通常是可以編碼避免的邏輯錯誤,具體根據需要來判斷是否需要捕獲,並不會在編譯期強制要求。
⑶Error是指正常情況下,不大可能出現的情況,絕大部分的Error都會導致程序處於非正常的、不可恢復的狀態。既然是非正常情況,不便於也不需要捕獲。常見的比如OutOfMemoryError之類都是Error的子類。
③ 簡單的類圖,列典型例子如下
④ 理解Java語言中操作Throwable的元素和實踐,掌握最基本的語法是必須的,如try-catch-finally塊,throw throws關鍵字等。
⑤ 實例
⑴下面的代碼反應了異常處理中哪些不當之處?
try{ //........ //....... Thread.sleep(1000L); }catch (Exception e){ //.... }
以上代碼違反了兩個基本原則
| 盡量不要捕獲Exception這樣的通用異常,而應該捕獲特定異常,這里面的Thread.sleep()拋出的是InterruptedException。原因是讓代碼能夠直觀的體現出盡量多的信息,不要太泛,不好定位。
|| 不要生吞異常,否則會導致難以診斷。如果不把異常拋出來,也沒有輸出到日志,程序可能會以不可控的方式結束,沒人能夠輕易判斷是哪里出了異常以及是什么原因導致了異常。
⑵
try{ //........ //....... }catch (IOException e){ e.printStackTrace(); }
這段代碼沒什么問題,但在產品代碼中一般不允許這樣處理,原因是標准出錯不是個合適的輸出選項,因為很難判斷到底輸出到哪里了。尤其是對分布式系統,無法找到堆棧痕跡,所以最好詳細的輸出到日志里。
⑶ Throw early,catch late
public void readPreferences(String fielName){ //........... InputStream in =new FileInputStream(fileName); // read the file.... }
如果filename是空,程序會拋出NullPointerException異常,但是沒第一個時間暴露問題,堆棧信息會令人非常費解,往往需要更復雜的定位修改如下:
public void readPreferences(String fielName){ Objects.requireNonNull(fileName); //........... InputStream in =new FileInputStream(fileName); // read the file.... }
至於catch late,最差的處理方式就是生吞異常了,本質上就是掩蓋問題。如果實在不知道如何處理,可以保留原有的case信息,直接再拋出或者構建新的異常出去。在更高層面,因為有了清晰的邏輯,會更青春合適的處理方式是什么。
⑥自定義異常
⑴是否需要定義成Checked Exception,因為這種類型設計的初衷更是為了從異常情況恢復,作為異常設計者,我們往往有充足信息進行分類
⑵在診斷信息的同時要考慮避免包含敏感信息,否則會導致潛在的安全問題。Java的標准類庫出錯信息不包含具體的機器名,端口,IP等,一個重要考量就是信息安全問題,還有用戶數據一般不包含在日志里的
⑦ 從性能角度審視Java的異常處理機制
⑴try-catch代碼段會產生額外的性能開銷,它會影響JVM對代碼進行優化,所以建議僅捕獲有必要的代碼段,盡量不要一個大的try包住整段的代碼;與此同時,利用異常控制代碼流程也不是一個好注意,這樣非常低效。
⑵Java每實例化一個Exception,都會對當時的棧進行快照,這是一個相對較重的操作,如果發生的非常頻繁,這個開銷就不能被忽略了。