您好,登錄后才能下訂單哦!
這篇文章主要介紹怎么處理Java異常,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
1.在finally塊中清理資源或使用Try-With-Resource語句
在try塊中使用資源是很頻繁的,比如InputStream,之后需要關閉它。這些情況中的一個常見錯誤是在try塊結束時關閉資源。
public void doNotCloseResourceInTry() { FileInputStream inputStream = null; try { File file = new File("./tmp.txt"); inputStream = new FileInputStream(file); // use the inputStream to read a file // do NOT do this inputStream.close(); } catch (FileNotFoundException e) { log.error(e); } catch (IOException e) { log.error(e); } }
問題是只要沒有拋出異常,這種方法似乎完全正常。try塊中的所有語句都將被執行,資源將被關閉。
但是你添加了try塊是有原因的。你調用一個或多個可能拋出異常的方法,或者你自己拋出異常。這意味著你可能無法到達try塊的末尾。因此,你將不會關閉資源。
因此,你應該將所有清理代碼放入finally塊或使用try-with-resource語句。
使用Finally塊
與try塊的最后幾行相比,finally塊始終執行。這可以在成功執行try塊之后或在catch塊中處理異常之后發生。因此,你可以確保清理所有已打開的資源。
public void closeResourceInFinally() { FileInputStream inputStream = null; try { File file = new File("./tmp.txt"); inputStream = new FileInputStream(file); // use the inputStream to read a file } catch (FileNotFoundException e) { log.error(e); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { log.error(e); } } } }
Java 7的Try-With-Resource
另一種選擇是try-with-resource語句,我在Java異常處理的介紹中對此進行了更詳細的解釋。
如果資源實現AutoCloseable接口,則可以使用它。這就是大多數Java標準資源所做的事情。當你在try子句中打開資源時,它將在try塊執行后自動關閉,或者處理異常。
public void automaticallyCloseResource() { File file = new File("./tmp.txt"); try (FileInputStream inputStream = new FileInputStream(file);) { // use the inputStream to read a file } catch (FileNotFoundException e) { log.error(e); } catch (IOException e) { log.error(e); } }
2.特定異常
拋出的異常越具體越好。請記住,不明白你代碼的同事,或者你可能在幾個月后需要調用你的方法并處理異常。
因此,請務必提供盡可能多的信息。這使你的API更易于理解。因此,你的方法的調用者將能夠更好地處理異常或通過額外的檢查來避免它。
因此,總是嘗試找到最適合你的異常事件的類,例如拋出NumberFormatException而不是IllegalArgumentException。并避免拋出非特定的異常。
public void doNotDoThis() throws Exception { ... } public void doThis() throws NumberFormatException { ... }
3.記錄你聲明的異常
無論何時在方法簽名中指定異常,都應該在Javadoc中記錄它。這與以前的最佳實踐具有相同的目標:為調用者提供盡可能多的信息,以便他可以避免或處理異常。
因此,請確保向Javadoc 添加@throws聲明并描述可能導致異常的情況。
/** * This method does something extremely useful ... * * @param input * @throws MyBusinessException if ... happens */ public void doSomething(String input) throws MyBusinessException { ... }
4.使用描述信息拋出異常
這種最佳實踐背后的想法類似于前兩種實踐。但是這次,你不向調用方提供有關方法的信息。每個必須了解在日志文件或監視工具中拋出異常時發生了什么的人都會讀取異常的消息。
因此,它應該盡可能準確地描述問題,并提供最相關的信息來理解異常事件。
別誤會我的意思; 你不應該寫一段文字。但是你應該用1-2個簡短的句子來解釋這個例外的原因。這有助于你的運營團隊了解問題的嚴重性,還可以讓你更輕松地分析任何服務事件。
如果拋出一個特定的異常,它的類名很可能已經描述了那種錯誤。因此,你無需提供大量其他信息。一個很好的例子是NumberFormatException。它會被類java.lang.Long的構造函數拋出,當你以錯誤的格式提供String參數。
try { new Long("xyz"); } catch (NumberFormatException e) { log.error(e); }
NumberFormatException類的名稱已經告訴你問題的類型。它的消息只需要提供導致問題的輸入字符串。如果異常類的名稱不具有表現力,則需要在消息中提供所需的信息。
17:17:26,386 ERROR TestExceptionHandling:52 - java.lang.NumberFormatException: For input string: "xyz"
5.優先捕獲最具體的異常
大多數IDE都可以幫助你實現這一最佳實踐。當你嘗試首先捕獲不太具體的異常時,它們提示無法訪問的代碼塊。
問題是只有匹配異常的第一個catch塊才會被執行。因此,如果首先捕獲IllegalArgumentException,則永遠不會到達應該處理更具體的NumberFormatException的catch塊,因為它是IllegalArgumentException的子類。
始終優先捕獲最具體的異常類,并將不太具體的catch塊添加到列表的末尾。
你可以在以下代碼段中看到此類try-catch語句的示例。第一個catch塊處理所有的NumberFormatException,第二個處理所有不是NumberFormatException的IllegalArgumentException 異常。
public void catchMostSpecificExceptionFirst() { try { doSomething("A message"); } catch (NumberFormatException e) { log.error(e); } catch (IllegalArgumentException e) { log.error(e) } }
6.Don't Catch Throwable
Throwable是所有異常和錯誤的超類。你可以在catch子句中使用它,但你永遠不應該這樣做!
如果在catch子句中使用Throwable,它不僅會捕獲所有異常; 它還會捕獲所有錯誤。JVM拋出錯誤以指示應用程序無法處理的嚴重問題。典型的例子是OutOfMemoryError或StackOverflowError。兩者都是由應用程序無法控制的情況引起的,無法處理。
所以,最好不要抓住Throwable,除非你完全確定你處于一個特殊情況,你可以或者需要處理錯誤。
public void doNotCatchThrowable() { try { // do something } catch (Throwable t) { // don't do this! } }
7.Don't Ignore Exceptions
你是否曾經分析過只有用例第一部分被執行的錯誤報告?
這通常是由忽略的異常引起的。開發人員可能非常確定它永遠不會被拋出并添加了一個不處理或記錄它的catch塊。當你找到這個代碼塊時,你很可能甚至會發現一個著名的“This will never happen
”的評論。
public void doNotIgnoreExceptions() { try { // do something } catch (NumberFormatException e) { // this will never happen } }
好吧,你可能正在分析一個不可能發生的問題。
所以,請永遠不要忽視異常。你不知道代碼將來會如何變化。有人可能會刪除阻止異常事件的驗證而不會認識到這會產生問題。或者拋出異常的代碼會被更改,現在拋出同一個類的多個異常,并且調用代碼不會阻止所有這些異常。
你至少應該寫一條日志消息,告訴大家不可思議的事情剛剛發生,而且有人需要檢查它。
public void logAnException() { try { // do something } catch (NumberFormatException e) { log.error("This should never happen: " + e); } }
8.Don't Log and Throw
這可能是此列表中最常被忽略的最佳做法。你可以找到許多代碼片段,甚至是catch,log和重新throw異常的庫。
try { new Long("xyz"); } catch (NumberFormatException e) { log.error(e); throw e; }
在發生異常時記錄異常可能會感覺很直接,然后重新拋出它以便調用者可以適當地處理它。但它會為同一個異常寫出多條錯誤消息。
17:44:28,945 ERROR TestExceptionHandling:65 - java.lang.NumberFormatException: For input string: "xyz"
Exception in thread "main" java.lang.NumberFormatException: For input string: "xyz"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:589)
at java.lang.Long.(Long.java:965)
at com.stackify.example.TestExceptionHandling.logAndThrowException(TestExceptionHandling.java:63)
at com.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58)
其他消息也不添加任何信息。如最佳實踐#4中所述,異常消息應描述異常事件。堆棧跟蹤告訴你拋出異常的類,方法和行。
如果需要添加其他信息,則應捕獲異常并將其包裝在自定義異常中。但請務必遵循最佳做法9。
public void wrapException(String input) throws MyBusinessException { try { // do something } catch (NumberFormatException e) { throw new MyBusinessException("A message that describes the error.", e); } }
因此,如果你想要處理它,只捕獲異常。否則,在方法簽名中指定它并讓調用者處理它。
9.在沒有消費的情況下包裝異常
有時候捕獲標準異常并將其包裝成自定義異常會更好。此類異常的典型示例是應用程序或框架特定的業務異常。這允許你添加其他信息,還可以為異常類實現特殊處理。
執行此操作時,請確保將原始異常設置為cause。該異常類提供了接受一個特定的構造方法的Throwable作為參數。否則,你將丟失原始異常的堆棧跟蹤和消息,這將導致難以分析導致異常的異常事件。
public void wrapException(String input) throws MyBusinessException { try { // do something } catch (NumberFormatException e) { throw new MyBusinessException("A message that describes the error.", e); } }
總結
正如所看到的,當你拋出或捕獲異常時,你應該考慮許多不同的事情。其中大多數都旨在提高代碼的可讀性或API的可用性。
異常通常同時是錯誤處理機制和通信媒介。因此,您應該確保與同事討論要應用的最佳實踐和規則,以便每個人都能理解通用概念并以相同的方式使用它們。
以上是“怎么處理Java異常”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。