Java 異??v然有很多好處,但是也應該恰當使用,本文將從程序性能優化、代碼結構優化的角度給出異常處理的一般規范。
成功的異常處理應該實現如下四個目標:
使程序代碼混亂程序最??;
捕獲并保留診斷信息;
通知合適的技術人員;
采用合適的方式結束異?;顒?。
為了達到這四個目標,讀者應該遵循以下規范。
不可否認,Java 的異常機制確實很方便,但濫用異常機制也會帶來一些負面影響。
過度使用異常主要體現在以下兩個方面:
把異常和普通錯誤混淆在一起,不再編寫任何錯誤處理代碼,而是以簡單地拋出異常來代替所有的錯誤處理。
使用異常處理來代替流程控制。
熟悉了異常使用方法后,程序員可能不再愿意編寫煩瑣的錯誤處理代碼,而是簡單地拋出異常。實際上這樣做是不對的,對于完全已知和普通的錯誤,應該編寫處理這種錯誤處理代碼,增加程序的健壯性;只有對外部的、不能確定和預知的運行時錯誤才使用異常。
下面一段代碼是從 Java 五子棋游戲中摘取出來的。當用戶輸入的坐標處已經有其它棋子的時候,會調用下面的代碼:
上面這種處理方式檢測到用戶試圖下棋的坐標點已經有棋子了,立即打印一條提示語句,并重新開始下一次循環。這種處理方式簡潔明了,邏輯清晰,提高運行效率。
如果將上面的處理機制改為如下方式:
上面的處理方式沒有提供有效的錯誤處理代碼,當程序檢測到用戶試圖下棋的坐標點已經有棋子時,并沒有提供相應的處理,而是簡單的拋出了一個異常。這種處理方式雖然簡單,但 Java 運行時接收到這個異常后,還需要進行相應的 catch 塊來捕獲該異常,所以運行效率要差一些。而且用戶下棋重復這個錯誤完全是預料的,所以程序完全可以針對該錯誤提供相應的處理,而不是拋出異常。
異常處理機制的效率比正常的流程控制效率差,所以不要使用異常處理來代替正常的程序流程控制。例如,對于以下代碼:
運行上面程序確實可以實現遍歷 arr 數組元素的功能,但這種寫法可讀性較差,而且運行效率也不高。程序完全有能力避免產生 ArrayIndexOutOfBoundsException 異常,程序“故意”制造這種異常,然后使用 catch 塊去捕獲該異常,這是不應該的。
將程序改為如下形式肯定要好得多。
異常只應該用于處理非正常的情況,不要使用異常處理來代替正常的流程控制。對于一些完全可預知,而且處理方式清楚的錯誤,程序應該提供相應的錯誤處理代碼,而不是將其籠統地稱為異常。
很多初學異常機制的讀者喜歡在 try 塊里放置大量的代碼,這樣看上去“很簡單“,但這種”簡單“只是一種假象,因為 try 塊里的代碼過于龐大,業務過于復雜,就會造成 try 塊中出現異常的可能性大大增加,從而導致分析異常原因的難度也大大增加。而且當 try 塊過于龐大時,就難免在 try 塊后緊跟大量的 catch 塊才可以針對不同的異常提供不同的處理邏輯。同一個 try 塊后緊跟大量的 catch 塊則需要分析它們之間的邏輯關系,反而增加了變成復雜度。
正確的做法是,把大塊的 try 塊分割成多個可能出現異常的程序段落,并把它們放在單獨的 try 塊中,從而分別捕獲并處理異常。
所謂 Catch All 語句指的是一種異常捕獲模塊,它可以處理程序發生的所有可能異常。例如,如下代碼片段:
不可否認,每個程序員都曾經用過這種異常處理方式,但在編寫關鍵程序時就應避免使用這種異常處理方式。
這種處理方式有如下兩點不足之處:
所有的異常都采用相同的處理方式,這將導致無法對不同的異常分情況處理,如果要分情況處理,則需要在 catch 塊中使用分支語句進行控制,這是得不償失的做法。
這種捕獲方式可能將程序中的錯誤、Runtime 異常等可能導致程序終止的情況全部捕獲到,從而“壓制”了異常。如果出現了一些“關鍵”異常,那么此異常也會被“靜悄悄”地忽略。
實際上,Catch All 語句不過是一種通過避免錯誤處理而加快編程進度的機制,應盡量避免在實際應用中使用這種語句。
不要忽略異常,既然已捕獲到異常,那 catch 塊理應處理并修復這個錯誤。catch 塊整個為空,或者僅僅打印出錯信息都是不妥的。程序出了錯誤,所有的人都看不到任何異常,但整個應用可能已經徹底壞了,這是最可怕的事情。
對異常進行合適的修復,然后繞過異常發生的地方繼續執行;或者用別的數據進行計算,以代替期望的方法返回值;或者提示用戶重新操作等??傊?,對于 Checked 異常,程序應該盡量修復。