您想了解的所有Throwable
本文是有關(guān)異常的教程。 但不是通常的一種。 其中有許多內(nèi)容可以告訴您異常的含義,如何拋出異常,捕獲異常,已檢查異常與運行時異常之間的區(qū)別,等等。 沒有必要了。 這對您來說也很無聊。 如果沒有,那么請閱讀其中的一本,并在您了解他們所教的內(nèi)容后再回來。 本文從這些教程的結(jié)尾處開始。 我們對Java異常進行了更深入的研究,您可以使用它們做什么,應(yīng)該使用它們做什么,以及它們可能沒有聽說的功能。 如果setStackTrace() , getCause()和getSuppressed()是您早餐時使用的方法,則可以跳過本文。 但是,如果不是這樣,并且您想對此有所了解,請繼續(xù)。 這篇文章很長。 寫作花了很長時間,而閱讀花了很長時間。 這是必需的。
介紹
在本文中,我們將討論異常以及Java異常可以做什么以及應(yīng)該做什么。 最簡單的情況是拋出一個然后捕獲它,但是存在更復(fù)雜的情況,例如設(shè)置原因或抑制異常。 我們將探討這些可能性,以及更多其他可能性。 為了發(fā)現(xiàn)可能性,我們將開發(fā)一個簡單的應(yīng)用程序,并逐步創(chuàng)建四個版本,進一步開發(fā)該應(yīng)用程序,并使用越來越多的異常處理可能性。 源代碼在存儲庫中可用:
https://github.com/verhas/BLOG/tree/master/exception_no_stack
不同的版本在不同的Java包中。 有些在不同版本中未更改的類要高出一個包,并且沒有版本化。
- 第一個版本v1只會引發(fā)en異常,并且應(yīng)用程序不會對其進行處理。 測試代碼期望測試設(shè)置拋出異常。 此版本是說明為什么我們需要更復(fù)雜的解決方案的基準。 我們將體驗到,沒有足夠的信息來了解實際問題發(fā)生在哪里的異常。
- 第二個版本v2在更高級別上捕獲了該異常,并引發(fā)了一個新異常,其中包含有關(guān)異常情況的更多信息,并且新異常中嵌入了原始異常作為原因。 這種方法提供了足夠的信息來跟蹤問題的位置,但是甚至可以對其進行增強,以便于閱讀和識別實際問題。
- v3將演示我們?nèi)绾涡薷男庐惓5膭?chuàng)建,以便更高級別的異常的堆棧跟蹤不會指向捕獲原始異常的位置,而是指向引發(fā)原始異常的位置。
- 最后,第四版v4將演示在異常情況下即使可能無法成功完成操作也可以繼續(xù)處理時如何抑制表達式。 這種“更進一步”使得最后可能有一個異常,該異常收集有關(guān)所有發(fā)現(xiàn)的異常情況的信息,而不僅是首次出現(xiàn)的信息。
如果您查看代碼,還將在此找到本文的原始文本,以及有助于維護代碼段的設(shè)置,這些代碼段將其從源代碼復(fù)制到文章中,從而使所有代碼段都保持最新。 對我們有用的工具是Java :: Geci。
樣品申請
我們使用異常來處理程序正常流程之外的內(nèi)容。 引發(fā)異常時,程序的正常流程將中斷,并且執(zhí)行將停止將異常轉(zhuǎn)儲到某些輸出。 也可以使用語言中內(nèi)置的try and catch命令對來捕獲這些異常。
try { ... some code ... ... even calling methods several level deep ... ... where exception may be thrown ... } catch (SomeException e){ ... code having access to the exception object 'e' and doing someting with it (handling) .... } 異常本身是Java中的對象,并且可以包含很多信息。 當(dāng)我們在代碼中捕獲異常時,我們可以訪問異常對象,并且代碼可以在特殊情況下也可以訪問異常對象所攜帶的參數(shù),從而采取行動。 可以實現(xiàn)我們自己的擴展Java的異常
java.lang.Throwable類或直接或傳遞擴展Throwable某些類。 (通常,我們擴展Exception類。)我們自己的實現(xiàn)可以包含許多描述異常情況性質(zhì)的參數(shù)。 為此,我們使用對象字段。
盡管異常可以承載的數(shù)據(jù)沒有限制,但是異常所包含的信息通常不超過消息和堆棧跟蹤。 在Throwable類中定義了其他參數(shù)的空間,例如導(dǎo)致當(dāng)前參數(shù)的異常( getCause() )或一系列抑制異常( getSuppressed() )。 很少使用它們,可能是因為開發(fā)人員不了解這些功能,并且因為大多數(shù)情況很簡單,不需要這些可能性。 我們將在本文中探討這些可能性,以便您不會屬于無知的開發(fā)人員,這些開發(fā)人員不僅僅因為他們不知道這些方法而就使用這些方法。
我們有一個示例應(yīng)用程序。 它不僅僅是在catch分支中引發(fā),捕獲和處理異常,該異常使代碼得以繼續(xù)。 這很簡單,并且在您第一次學(xué)習(xí)Java編程時已閱讀的教程中對此進行了解釋。
我們的示例應(yīng)用程序?qū)⒏訌?fù)雜。 我們將在目錄中列出文件,讀取行,并計算wtf字符串的數(shù)量。 通過這種方式,我們可以自動執(zhí)行代碼審查過程質(zhì)量度量(開玩笑)。 據(jù)說在代碼審查期間,代碼質(zhì)量與WTF的數(shù)量成反比。
解決方案包含
- 可以列出文件的FileLister ,
- 可以讀取文件的FileReader ,
- 一個LineWtfCounter ,它將在一行中計算wtf ,
- 一個FileWtfCounter ,它將使用上一個類對整個文件中列出行的所有wtf進行計數(shù),最后,
- 一個ProjectWtfCounter ,它使用文件級計數(shù)器對整個項目中的wtf進行計數(shù),列出所有文件。
版本1,投擲
該應(yīng)用程序的功能非常簡單,并且因為我們專注于異常處理,所以實現(xiàn)也很簡單。 例如,文件列表類很簡單,如下所示:
package javax0.blog.demo.throwable; import java.util.List; public class FileLister { public FileLister() { } public List<String> list() { return List.of( "a.txt" , "b.txt" , "c.txt" ); } }文件系統(tǒng)中有三個文件a.txt , b.txt和c.txt 。 當(dāng)然,這是一個模擬,但是在這種情況下,我們不需要任何更復(fù)雜的方法來演示異常處理。 同樣, FileReader也是一種模擬實現(xiàn),僅用于演示目的:
package javax0.blog.demo.throwable.v1; import java.util.List; public class FileReader { final String fileName; public FileReader(String fileName) { this .fileName = fileName; } public List<String> list() { if (fileName.equals( "a.txt" )) { return List.of( "wtf wtf" , "wtf something" , "nothing" ); } if (fileName.equals( "b.txt" )) { return List.of( "wtf wtf wtf" , "wtf something wtf" , "nothing wtf" ); } if (fileName.equals( "c.txt" )) { return List.of( "wtf wtf wtf" , "wtf something wtf" , "nothing wtf" , "" ); } throw new RuntimeException( "File is not found: " + fileName); } }計算一行中wtf出現(xiàn)次數(shù)的計數(shù)器是
package javax0.blog.demo.throwable.v1; public class LineWtfCounter { private final String line; public LineWtfCounter(String line) { this .line = line; } public static final String WTF = "wtf" ; public static final int WTF_LEN = WTF.length(); public int count() { if (line.length() == 0 ) { throw new LineEmpty(); } // the actual lines are removed from the documentation snippet } }為了節(jié)省空間并專注于我們的主題,代碼段不顯示實際的邏輯(由Java :: Geci自動刪除)。 讀者可以創(chuàng)建一個代碼,該代碼實際計算字符串中wtf子字符串的數(shù)量,或者簡單地計算“ wtf”。 即使讀者不能編寫這樣的代碼,也可以從本文開頭提到的存儲庫中獲得。
我們應(yīng)用程序中的邏輯表明,如果文件中的某行長度為零,則這是一種特殊情況。 在那種情況下,我們拋出一個異常。
通常,這種情況并不能證明是一個例外,我承認這是一個虛構(gòu)的示例,但是我們需要一些簡單的方法。 如果行的長度為零,則拋出LineEmpty異常。 (我們沒有列出LineEmpty異常的代碼。它在代碼存儲庫中,它很簡單,沒什么特別的。它擴展了RuntimeException ,無需聲明我們將其放置在何處。)如果您查看FileReader的模擬實現(xiàn),則您會看到我們在文件c.txt中插入了空行。
使用行級計數(shù)器的文件級計數(shù)器如下:
package javax0.blog.demo.throwable.v1; public class FileWtfCounter { // fileReader injection is omitted for brevity public int count() { final var lines = fileReader.list(); int sum = 0 ; for ( final var line : lines) { sum += new LineWtfCounter(line).count(); } return sum; } }(同樣,從打印輸出中跳過了一些瑣碎的行。)
這是應(yīng)用程序的第一個版本。 它沒有任何特殊的異常處理。 它只是對行計數(shù)器返回的值求和,如果較低級別有異常,則行wtf計數(shù)器會自動向上傳播。 在此級別上,我們不會以任何方式處理該異常。
項目級計數(shù)器非常相似。 它使用文件計數(shù)器并對結(jié)果求和。
package javax0.blog.demo.throwable.v1; import javax0.blog.demo.throwable.FileLister; public class ProjectWftCounter { // fileLister injection is omitted for brevity public int count() { final var fileNames = fileLister.list(); int sum = 0 ; for ( final var fileName : fileNames) { sum += new FileWtfCounter( new FileReader(fileName)).count(); } return sum; } }我們使用簡單的測試代碼對其進行測試:
package javax0.blog.demo.throwable.v1; import javax0.blog.demo.throwable.FileLister; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; public class TestWtfCounter { @Test @DisplayName ( "Throws up for a zero length line" ) void testThrowing() { Throwable thrown = catchThrowable(() -> new ProjectWftCounter( new FileLister()) .count()); assertThat(thrown).isInstanceOf(LineEmpty. class ); thrown.printStackTrace(); } }單元測試通常不應(yīng)具有堆棧跟蹤打印。 在這種情況下,我們可以演示所拋出的內(nèi)容。 錯誤中的堆棧跟蹤將向我們顯示以下錯誤:
javax0.blog.demo.throwable.v1.LineEmpty: There is a zero length line at javax0.blog.demo.throwable.v1.LineWtfCounter.count(LineWtfCounter.java:18) at javax0.blog.demo.throwable.v1.FileWtfCounter.count(FileWtfCounter.java:19) at javax0.blog.demo.throwable.v1.ProjectWftCounter.count(ProjectWftCounter.java:22) at javax0.blog.demo.throwable.v1.TestWtfCounter.lambda$testThrowing$0(TestWtfCounter.java:18) at org.assertj.core.api.ThrowableAssert.catchThrowable(ThrowableAssert.java:62) ... at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)這個異常有點問題。 當(dāng)我們使用此代碼時,它不會告訴我們有關(guān)有問題的實際文件和行的任何信息。 如果有一個空文件,我們必須檢查所有文件和所有行。 為此編寫一個應(yīng)用程序不是太困難,但是我們不想代替創(chuàng)建該應(yīng)用程序的程序員來工作。 如果有例外,我們希望該例外能夠為我們提供足夠的信息,以成功解決該問題。 應(yīng)用程序必須告訴我哪個文件和哪一行有問題。
版本2,設(shè)置原因
為了在異常中提供信息,我們必須收集它并將其插入異常中。 這是我們在第二版應(yīng)用程序中所做的。
第一個版本中的異常不包含文件名或行號,因為代碼未將其放在此處。 該代碼有這樣做的充分理由。 引發(fā)異常的位置的代碼沒有信息,因此無法將其沒有的信息插入異常。
一種有利可圖的方法是將該信息與其他參數(shù)一起傳遞,以便在發(fā)生異常時代碼可以將此信息插入到異常中。 我不推薦這種方法。 如果您查看我在GitHub上發(fā)布的源代碼,則可能會找到這種做法的示例。 我不為他們感到驕傲,對不起。
通常,我建議異常處理不應(yīng)干擾應(yīng)用程序的主數(shù)據(jù)流。 必須將其分開,因為這是一個單獨的問題。
解決方案是在多個級別上處理異常,在每個級別上添加實際可用的信息。 為此,我們修改了FileWtfCounter和ProjectWftCounter類。
ProjectWftCounter的代碼如下:
package javax0.blog.demo.throwable.v2; public class FileWtfCounter { // some lines deleted ... public int count() { final var lines = fileReader.list(); int sum = 0 ; int lineNr = 1 ; for ( final var line : lines) { try { sum += new LineWtfCounter(line).count(); } catch (LineEmpty le){ throw new NumberedLineEmpty(lineNr,le); } lineNr ++; } return sum; } }該代碼捕獲了向空行發(fā)出信號并拋出新的異常的信號,該異常已經(jīng)有一個參數(shù):該行的序列號。
此異常的代碼LineEmpty那樣瑣碎,因此在此處列出:
package javax0.blog.demo.throwable.v2; public class NumberedLineEmpty extends LineEmpty { final protected int lineNr; public NumberedLineEmpty( int lineNr, LineEmpty cause) { super (cause); this .lineNr = lineNr; } @Override public String getMessage() { return "line " + lineNr + ". has zero length" ; } }我們將行號存儲在int字段中,該字段為final 。 我們這樣做是因為
- 如果可能,使用final變量
- 如果可能,在對象上使用基元
- 盡可能長時間以原始形式存儲信息,因此不限制其使用
前兩個標準是通用的。 盡管不是特定于異常處理,但在這種情況下,最后一個是特殊的。 但是,當(dāng)我們處理異常時,僅生成包含行號的消息而不是使異常類的結(jié)構(gòu)復(fù)雜化是非常有利可圖的。 畢竟,我們永遠不會的推理
除了將異常打印到屏幕上之外,將異常用于任何其他用途。 或不? 這取決于。 首先,永不言敗。 再三考慮:如果我們將行號編碼到消息中,那么可以肯定的是,除了將其打印給用戶之外,我們絕不會將其用于任何其他用途。 那是因為我們不能將其用于其他任何用途。 我們限制自己。 今天的程序員限制了將來的程序員對數(shù)據(jù)做有意義的事情。
您可能會爭辯說這是YAGNI 。 當(dāng)我們要使用它時,我們應(yīng)該注意將其存儲為整數(shù),并且在此刻關(guān)心它還為時過早,這只是浪費時間。 你是對的! 同時,創(chuàng)建額外字段和計算異常信息的文本版本的getMessage()方法的人也是正確的。 有時,YAGNI與精心設(shè)計的良好風(fēng)格之間的界限很細。 YAGNI是為了避免以后不再需要的復(fù)雜代碼(除了在創(chuàng)建代碼時,您認為自己會需要)。 在此示例中,我認為上述帶有一個額外的int字段的異常不是“復(fù)雜”的。
我們在“項目”級別有一個類似的代碼,我們在其中處理所有文件。 ProjectWftCounter的代碼將是
package javax0.blog.demo.throwable.v2; import javax0.blog.demo.throwable.FileLister; public class ProjectWftCounter { // some lines deleted ... public int count() { final var fileNames = fileLister.list(); int sum = 0 ; for ( final var fileName : fileNames) { try { sum += new FileWtfCounter( new FileReader(fileName)).count(); } catch (NumberedLineEmpty nle) { throw new FileNumberedLineEmpty(fileName, nle); } } return sum; } }在這里,我們知道文件的名稱,因此我們可以擴展信息,將其添加到異常中。
FileNumberedLineEmpty異常也類似于NumberedLineEmpty的代碼。 這是FileNumberedLineEmpty的代碼:
package javax0.blog.demo.throwable.v2; public class FileNumberedLineEmpty extends NumberedLineEmpty { final protected String fileName; public FileNumberedLineEmpty(String fileName, NumberedLineEmpty cause) { super (cause.lineNr, cause); this .fileName = fileName; } @Override public String getMessage() { return fileName + ":" + lineNr + " is empty" ; } }現(xiàn)在,我將吸引您關(guān)注這樣一個事實,即我們創(chuàng)建的異常也屬于繼承層次結(jié)構(gòu)。 隨著我們收集和存儲的信息的擴展,它們擴展了另一個,因此:
FileNumberedLineEmpty - extends -> NumberedLineEmpty - extends -> LineEmpty如果使用這些方法的代碼期望并嘗試處理LineEmpty異常,那么即使我們拋出更詳細和專門的異常,它也可以執(zhí)行。 如果代碼想要使用額外的信息,那么最終,它必須知道實際實例不是LineEmpty而是更專業(yè)的NumberedLineEmpty或FileNumberedLineEmpty 。 但是,如果只想打印出來,得到消息,則可以將異常作為LineEmpty的實例進行處理是絕對好的。 即使這樣,由于OO編程多態(tài)性,消息仍將包含人類可讀形式的額外信息。
吃的時候有布丁的證明。 我們可以通過簡單的測試運行代碼。 測試代碼與以前的版本相同,唯一的例外是預(yù)期的異常類型為FileNumberedLineEmpty而不是LineEmpty 。 但是,打印輸出很有趣:
javax0.blog.demo.throwable.v2.FileNumberedLineEmpty: c.txt:4 is empty at javax0.blog.demo.throwable.v2.ProjectWftCounter.count(ProjectWftCounter.java:22) at javax0.blog.demo.throwable.v2.TestWtfCounter.lambda$testThrowing$0(TestWtfCounter.java:17) at org.assertj.core.api.ThrowableAssert.catchThrowable(ThrowableAssert.java:62) ... at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58) Caused by: javax0.blog.demo.throwable.v2.NumberedLineEmpty: line 4. has zero length at javax0.blog.demo.throwable.v2.FileWtfCounter.count(FileWtfCounter.java:21) at javax0.blog.demo.throwable.v2.ProjectWftCounter.count(ProjectWftCounter.java:20) ... 68 more ... 68 Caused by: javax0.blog.demo.throwable.v2.LineEmpty: There is a zero length line at javax0.blog.demo.throwable.v2.LineWtfCounter.count(LineWtfCounter.java:15) at javax0.blog.demo.throwable.v2.FileWtfCounter.count(FileWtfCounter.java:19) ... 69 more ... 69我們可以對這個結(jié)果感到滿意,因為我們立即看到導(dǎo)致問題的文件是c.txt ,第四行是罪魁禍首。 另一方面,當(dāng)我們想看看引發(fā)異常的代碼時,我們不會感到高興。 在將來的某個時候,我們可能不記得為什么一條線的長度不能為零。 在這種情況下,我們想看一下代碼。 在那里,我們只會看到捕獲并重新拋出異常。 幸運的是,這是有原因的,但是實際上直到到達LineWtfCounter.java:15的真正問題的代碼為止,這實際上是三個步驟。
有人會對捕獲和拋出異常的代碼感興趣嗎? 也許是吧。 也許沒有。 在我們的案例中,我們決定將不會有人對該代碼感興趣,而不是處理一長串列出有罪原因的異常鏈,而是將異常的堆棧跟蹤更改為引發(fā)異常的堆棧跟蹤
例外。
版本3,設(shè)置堆棧跟蹤
在此版本中,我們僅更改以下兩個異常的代碼: NumberedLineEmpty和FileNumberedLineEmpty 。 現(xiàn)在,他們不僅擴展了彼此,又擴展了另一個LineEmpty而且還將自己的堆棧跟蹤設(shè)置為引起異常的值。
這是NumberedLineEmpty的新版本:
package javax0.blog.demo.throwable.v3; public class NumberedLineEmpty extends LineEmpty { final protected int lineNr; public NumberedLineEmpty( int lineNr, LineEmpty cause) { super (cause); this .setStackTrace(cause.getStackTrace()); this .lineNr = lineNr; } // getMessage() same as in v2 @Override public Throwable fillInStackTrace() { return this ; } }這是FileNumberedLineEmpty的新版本:
package javax0.blog.demo.throwable.v3; public class FileNumberedLineEmpty extends NumberedLineEmpty { final protected String fileName; public FileNumberedLineEmpty(String fileName, NumberedLineEmpty cause) { super (cause.lineNr, cause); this .setStackTrace(cause.getStackTrace()); this .fileName = fileName; } // getMessage(), same as in v2 @Override public Throwable fillInStackTrace() { return this ; } } 有一個公共的setStackTrace()方法,可用于設(shè)置異常的堆棧跟蹤。 有趣的是,此方法實際上是public ,不受保護。 該方法是public這一事實意味著可以從外部設(shè)置任何異常的堆棧跟蹤。 這樣做(可能)違反了封裝規(guī)則。
不過,它在那里,如果它在那里,那么我們可以使用它來將異常的堆棧跟蹤設(shè)置為與引起異常的堆棧跟蹤相同。
這些異常類中還有另一段有趣的代碼。 這是公共的fillInStackTrace()方法。 如果我們像上面那樣實現(xiàn)這一點,那么我們可以節(jié)省異常在對象構(gòu)造過程中花費的時間,以收集我們自己替換并丟棄的原始堆棧跟蹤。
當(dāng)我們創(chuàng)建一個新的異常時,構(gòu)造函數(shù)會調(diào)用本機方法來填充堆棧跟蹤。 如果查看類java.lang.Throwable的默認構(gòu)造函數(shù),您會發(fā)現(xiàn)實際上這就是它的全部功能(Java 14 OpenJDK):
public Throwable() { fillInStackTrace(); }方法fillInStackTrace()不是本機的,但這是實際上調(diào)用完成工作的本機fillInStackTrace(int)方法的方法。 這是完成的過程:
public synchronized Throwable fillInStackTrace() { if (stackTrace != null || backtrace != null /* Out of protocol state */ ) { fillInStackTrace( 0 ); stackTrace = UNASSIGNED_STACK; } return this ; }它里面有一些“魔術(shù)”,它如何設(shè)置字段stackTrace但是到目前為止,這并不真正重要。 但是,請務(wù)必注意,方法fillInStackTrace()是public 。 這意味著它可以被覆蓋。 (為此, protected就足夠了,但public更是允許。)
我們還設(shè)置了引起異常,在這種情況下,它將具有相同的堆棧跟蹤。 運行測試(類似于我們之前僅列出其中一項的測試),我們將打印出堆棧:
javax0.blog.demo.throwable.v3.FileNumberedLineEmpty: c.txt:4 is empty at javax0.blog.demo.throwable.v3.LineWtfCounter.count(LineWtfCounter.java:15) at javax0.blog.demo.throwable.v3.FileWtfCounter.count(FileWtfCounter.java:16) at javax0.blog.demo.throwable.v3.ProjectWftCounter.count(ProjectWftCounter.java:19) at javax0.blog.demo.throwable.v3.TestWtfCounter.lambda$testThrowing$0(TestWtfCounter.java:17) at org.assertj.core.api.ThrowableAssert.catchThrowable(ThrowableAssert.java:62) ... at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58) Caused by: javax0.blog.demo.throwable.v3.NumberedLineEmpty: line 4. has zero length ... 71 more ... 71 Caused by: javax0.blog.demo.throwable.v3.LineEmpty: There is a zero length line ... 71 more ... 71毫不奇怪,我們有一個FileNumberedLineEmpty ,它的堆棧跟蹤從代碼行LineWtfCounter.java:15 ,不會引發(fā)該異常。 當(dāng)我們看到這一點時,可能會有一些辯論:
- 當(dāng)我們覆蓋堆棧跟蹤時,為什么我們需要在原始文件上附加引起異常的原因? (我們不。)
- 這是一個干凈的解決方案嗎? 堆棧跟蹤源自沒有引發(fā)該異常的行可能會造成混淆。
讓我們用這些問題來回答這些問題,是的,它們是出于演示目的所必需的,在實際的應(yīng)用程序中,每個程序員都可以決定是否要使用這樣的解決方案。
這是我們可以獲得的最佳解決方案嗎? 可能不是,因為正如我所承諾的,我們擁有該應(yīng)用程序的第四版。
版本4,抑制異常
創(chuàng)建模擬FileReader我們非常樂觀。 我們假設(shè)只有一行的長度為零。 如果有不止一條這樣的線怎么辦? 在這種情況下,應(yīng)用程序?qū)牡谝粋€停止。 用戶修復(fù)了以下錯誤:要么在行中添加了一些字符,以使該字符不再為空,要么完全刪除了該錯誤,從而使該字符不再為行。 然后,用戶再次運行該應(yīng)用程序以獲取異常中的第二個位置。 如果有很多這樣的行要糾正,那么此過程可能很麻煩。 您還可以想象真實的應(yīng)用程序中的代碼可能運行很長時間,更不用說要花費幾個小時了。 僅為了獲得問題的下一個位置而執(zhí)行該應(yīng)用程序就是在浪費人力,浪費CPU時鐘,能源,從而不必要地清潔產(chǎn)生氧氣的CO2。
我們可以做的是,更改應(yīng)用程序,以便在有空行的情況下繼續(xù)處理該應(yīng)用程序,并且僅在處理完所有文件和所有行之后,它會引發(fā)異常,列出所有在過程中發(fā)現(xiàn)的空行。 有兩種方法。 一種是創(chuàng)建一些數(shù)據(jù)結(jié)構(gòu)并將信息存儲在其中,然后在處理結(jié)束時,應(yīng)用程序可以查看該數(shù)據(jù)結(jié)構(gòu),并在其中存在有關(guān)某些空行的任何信息時引發(fā)異常。 另一種是使用異常類提供的結(jié)構(gòu)來存儲信息。
好處是使用異常類提供的結(jié)構(gòu)是
- 該結(jié)構(gòu)已經(jīng)存在,無需重新發(fā)明輪子,
- 它是由許多經(jīng)驗豐富的開發(fā)人員精心設(shè)計的,并且已經(jīng)使用了數(shù)十年,可能是正確的結(jié)構(gòu),
- 該結(jié)構(gòu)的通用性足以容納其他類型的異常,不僅是我們當(dāng)前擁有的異常,而且數(shù)據(jù)結(jié)構(gòu)不需要任何更改。
讓我們討論最后一點。 稍后可能會發(fā)生的情況是,我們決定包含WTF所有資本的行也是例外的,應(yīng)該拋出異常。 在這種情況下,如果我們決定手工制作這些結(jié)構(gòu),則可能需要修改存儲這些錯誤情況的數(shù)據(jù)結(jié)構(gòu)。 如果我們使用Throwable類的抑制的異常,則沒有其他事情要做。 有一個異常,我們將其捕獲(如您將在示例中很快看到的那樣),將其存儲,然后將其作為抑制的異常附加到摘要異常的末尾。 當(dāng)這個演示應(yīng)用程序極不可能擴展時,我們是否會考慮YAGNI? 是的,不是,通常沒有關(guān)系。 當(dāng)您花時間和精力過早開發(fā)某些東西時,YAGNI通常是一個問題。 在開發(fā)中以及后來的維護中,這是一筆額外的費用。 當(dāng)我們只使用已經(jīng)存在的更簡單的東西時,則不是YAGNI使用它。 它對我們使用的工具非常聰明并且知識淵博。
讓我們看一下修改后的FileReader ,這次它已經(jīng)在許多文件中返回許多空行:
package javax0.blog.demo.throwable.v4; import java.io.FileNotFoundException; import java.util.List; public class FileReader { final String fileName; public FileReader(String fileName) { this .fileName = fileName; } public List<String> list() { if (fileName.equals( "a.txt" )) { return List.of( "wtf wtf" , "wtf something" , "" , "nothing" ); } if (fileName.equals( "b.txt" )) { return List.of( "wtf wtf wtf" , "" , "wtf something wtf" , "nothing wtf" , "" ); } if (fileName.equals( "c.txt" )) { return List.of( "wtf wtf wtf" , "" , "wtf something wtf" , "nothing wtf" , "" ); } throw new RuntimeException( "File is not found: " + fileName); } }現(xiàn)在,所有三個文件都包含空行。 我們不需要修改LineWtfCounter計數(shù)器。 空行時,我們拋出異常。 在此級別上,沒有任何方法可以抑制此異常。 我們無法在此處收集任何例外列表。 我們只關(guān)注可能為空的一行。
FileWtfCounter的情況不同:
package javax0.blog.demo.throwable.v4; public class FileWtfCounter { private final FileReader fileReader; public FileWtfCounter(FileReader fileReader) { this .fileReader = fileReader; } public int count() { final var lines = fileReader.list(); NumberedLinesAreEmpty exceptionCollector = null ; int sum = 0 ; int lineNr = 1 ; for ( final var line : lines) { try { sum += new LineWtfCounter(line).count(); } catch (LineEmpty le){ final var nle = new NumberedLineEmpty(lineNr,le); if ( exceptionCollector == null ){ exceptionCollector = new NumberedLinesAreEmpty(); } exceptionCollector.addSuppressed(nle); } lineNr ++; } if ( exceptionCollector != null ){ throw exceptionCollector; } return sum; } }捕獲LineEmpty異常時,會將其存儲在局部變量exceptionCollector引用的聚合exceptionCollector 。 如果沒有exceptionCollector則在添加捕獲到的異常之前先創(chuàng)建一個,以避免NPE。 在處理的最后,當(dāng)我們處理所有行時,我們可能將許多異常添加到摘要異常exceptionCollector 。 如果存在,則將其拋出。
同樣, ProjectWftCounter收集由不同F(xiàn)ileWtfCounter實例引發(fā)的所有異常,并且在處理結(jié)束時,它將引發(fā)摘要異常,如以下代碼行所示:
package javax0.blog.demo.throwable.v4; import javax0.blog.demo.throwable.FileLister; public class ProjectWftCounter { private final FileLister fileLister; public ProjectWftCounter(FileLister fileLister) { this .fileLister = fileLister; } public int count() { final var fileNames = fileLister.list(); FileNumberedLinesAreEmpty exceptionCollector = null ; int sum = 0 ; for ( final var fileName : fileNames) { try { sum += new FileWtfCounter( new FileReader(fileName)).count(); } catch (NumberedLinesAreEmpty nle) { if ( exceptionCollector == null ){ exceptionCollector = new FileNumberedLinesAreEmpty(); } exceptionCollector.addSuppressed(nle); } } if ( exceptionCollector != null ){ throw exceptionCollector; } return sum; } }現(xiàn)在,我們已經(jīng)將所有有問題的行收集到一個巨大的異常結(jié)構(gòu)中,我們應(yīng)該得到一個堆棧跟蹤:
javax0.blog.demo.throwable.v4.FileNumberedLinesAreEmpty: There are empty lines at javax0.blog.demo.throwable.v4.ProjectWftCounter.count(ProjectWftCounter.java:24) at javax0.blog.demo.throwable.v4.TestWtfCounter.lambda$testThrowing$0(TestWtfCounter.java:17) at org.assertj.core.api.ThrowableAssert.catchThrowable(ThrowableAssert.java:62) at org.assertj.core.api.AssertionsForClassTypes.catchThrowable(AssertionsForClassTypes.java:750) at org.assertj.core.api.Assertions.catchThrowable(Assertions.java:1179) at javax0.blog.demo.throwable.v4.TestWtfCounter.testThrowing(TestWtfCounter.java:15) at java.base /jdk .internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base /jdk .internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base /jdk .internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base /java .lang.reflect.Method.invoke(Method.java:564) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686) at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84) at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115) at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:205) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:201) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) at java.base /java .util.ArrayList.forEach(ArrayList.java:1510) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) at java.base /java .util.ArrayList.forEach(ArrayList.java:1510) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248) at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211) at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58) Suppressed: javax0.blog.demo.throwable.v4.NumberedLinesAreEmpty at javax0.blog.demo.throwable.v4.FileWtfCounter.count(FileWtfCounter.java:22) at javax0.blog.demo.throwable.v4.ProjectWftCounter.count(ProjectWftCounter.java:21) ... 68 more ... 68 Suppressed: javax0.blog.demo.throwable.v4.NumberedLineEmpty: line 3. at javax0.blog.demo.throwable.v4.LineWtfCounter.count(LineWtfCounter.java:15) at javax0.blog.demo.throwable.v4.FileWtfCounter.count(FileWtfCounter.java:18) ... 69 more ... 69 Caused by: javax0.blog.demo.throwable.v4.LineEmpty: There is a zero length line Suppressed: javax0.blog.demo.throwable.v4.NumberedLinesAreEmpty at javax0.blog.demo.throwable.v4.FileWtfCounter.count(FileWtfCounter.java:22) at javax0.blog.demo.throwable.v4.ProjectWftCounter.count(ProjectWftCounter.java:21) ... 68 more ... 68 Suppressed: javax0.blog.demo.throwable.v4.NumberedLineEmpty: line 2. at javax0.blog.demo.throwable.v4.LineWtfCounter.count(LineWtfCounter.java:15) at javax0.blog.demo.throwable.v4.FileWtfCounter.count(FileWtfCounter.java:18) ... 69 more ... 69 Caused by: javax0.blog.demo.throwable.v4.LineEmpty: There is a zero length line Suppressed: javax0.blog.demo.throwable.v4.NumberedLineEmpty: line 5. at javax0.blog.demo.throwable.v4.LineWtfCounter.count(LineWtfCounter.java:15) at javax0.blog.demo.throwable.v4.FileWtfCounter.count(FileWtfCounter.java:18) ... 69 more ... 69 Caused by: javax0.blog.demo.throwable.v4.LineEmpty: There is a zero length line Suppressed: javax0.blog.demo.throwable.v4.NumberedLinesAreEmpty at javax0.blog.demo.throwable.v4.FileWtfCounter.count(FileWtfCounter.java:22) at javax0.blog.demo.throwable.v4.ProjectWftCounter.count(ProjectWftCounter.java:21) ... 68 more ... 68 Suppressed: javax0.blog.demo.throwable.v4.NumberedLineEmpty: line 2. at javax0.blog.demo.throwable.v4.LineWtfCounter.count(LineWtfCounter.java:15) at javax0.blog.demo.throwable.v4.FileWtfCounter.count(FileWtfCounter.java:18) ... 69 more ... 69 Caused by: javax0.blog.demo.throwable.v4.LineEmpty: There is a zero length line Suppressed: javax0.blog.demo.throwable.v4.NumberedLineEmpty: line 5. at javax0.blog.demo.throwable.v4.LineWtfCounter.count(LineWtfCounter.java:15) at javax0.blog.demo.throwable.v4.FileWtfCounter.count(FileWtfCounter.java:18) ... 69 more ... 69 Caused by: javax0.blog.demo.throwable.v4.LineEmpty: There is a zero length line這次,我沒有刪除任何線條以使您感覺到它在肩上的重量。 現(xiàn)在您可能會開始考慮使用異常結(jié)構(gòu)而不是僅包含我們所需要的信息的整潔,苗條的專用數(shù)據(jù)結(jié)構(gòu)是否值得。 如果您開始這樣認為, 那就停止它 。 不要這樣 問題(如果有的話)不是我們有太多信息。 問題在于我們的表達方式。 為了克服它,解決方案不是將嬰兒洗澡水倒掉……多余的信息,而是以更具可讀性的方式表示出來。 如果應(yīng)用程序很少遇到許多空行,那么對堆棧跟蹤進行讀取可能不會給用戶帶來難以承受的負擔(dān)。 如果這是一個經(jīng)常出現(xiàn)的問題,并且您希望對用戶(客戶,支付賬單的用戶)友好,那么,也許不錯的異常結(jié)構(gòu)打印機是一個不錯的解決方案。
我們在項目中實際上有一個適合您
javax0.blog.demo.throwable.v4.ExceptionStructurePrettyPrinter
您可以隨意使用甚至修改。 這樣,先前“可怕的”堆棧跟蹤的打印輸出將打印為:
FileNumberedLinesAreEmpty( "There are empty lines" ) Suppressed: NumberedLineEmpty( "line 3." ) Caused by:LineEmpty( "There is a zero length line" ) Suppressed: NumberedLineEmpty( "line 2." ) Caused by:LineEmpty( "There is a zero length line" ) Suppressed: NumberedLineEmpty( "line 5." ) Caused by:LineEmpty( "There is a zero length line" ) Suppressed: NumberedLineEmpty( "line 2." ) Caused by:LineEmpty( "There is a zero length line" ) Suppressed: NumberedLineEmpty( "line 5." ) Caused by:LineEmpty( "There is a zero length line" )這樣,我們就結(jié)束了練習(xí)。 我們逐步完成了以下步驟:從v1簡單地引發(fā)和捕獲異常,到v2設(shè)置導(dǎo)致異常的娃套風(fēng)格, v3更改嵌入異常的堆棧跟蹤,最后v4存儲我們在處理過程中收集的所有抑制的異常。 您現(xiàn)在可以做的是下載項目,進行操作,檢查堆棧跟蹤,修改代碼,等等。 或者繼續(xù)閱讀,我們有一些有關(guān)異常的額外信息,這些基本級教程很少討論這些異常,也值得閱讀最后的總結(jié)部分。
有關(guān)異常的其他注意事項
在本節(jié)中,我們將告訴您一些關(guān)于異常的基本Java教程中并不為人們所熟知的信息,而這些信息通常是缺失的。
JVM中沒有檢查異常之類的東西
除非方法聲明明確指出可能發(fā)生這種情況,否則無法從Java方法中引發(fā)已檢查的異常。 有趣的是,JVM不了解檢查異常的概念。 這是Java編譯器處理的事情,但是當(dāng)代碼進入JVM時,不會對此進行檢查。
Throwable (checked) <-- Exception (checked) <-- RuntimeException (unchecked) <-- Other Exceptions (checked) <-- Error (unchecked) 異常類的結(jié)構(gòu)如上所述。 異常的根類是Throwable 。 Any object that is an instance of a class, which extends directly or indirectly the Throwable class can be thrown. The root class Throwable is checked, thus if an instance of it is thrown from a method, then it has to be declared.
If any class extends this class directly and is thrown from a method then, again it has to be declared. Except if the object is also an instance of RuntimeException or Error . In that case the exception or error is not checked and can be thrown without declaring on the throwing method.
The idea of checked exception is controversial. There are advantages of its use but there are many languages that do not have the notion of it. This is the reason why the JVM does not enforce the declaration of checked exceptions. If it did it would not be possible reasonably to generate JVM code from languages that do not require exceptions declared and want to interoperate with the Java exceptions. Checked exceptions also cause a lot of headaches when we are using streams in Java.
It is possible to overcome of checked exceptions. A method created with some hack, or simply in a JVM language other than Java can throw a checked exception even if the method does not declare the exception to be thrown. The hacky way uses a simple static utility method, as listed in the following code snippet:
package javax0.blog.demo.throwable.sneaky; public class SneakyThrower { public static <E extends Throwable> E throwSneaky(Throwable e) throws E { throw (E) e; } }When a code throws a checked exception, for example Exception then passing it to throwSneaky() will fool the compiler. The compiler will look at the declaration of the static method and cannot decide if the Throwable it throws is checked or not. That way it will not require the declaration of the exception in the throwing method.
The use of this method is very simple and is demonstrated with the following unit test code:
package javax0.blog.demo.throwable.sneaky; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static javax0.blog.demo.throwable.sneaky.SneakyThrower.throwSneaky; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; public class TestSneaky { @DisplayName ( "Can throw checked exception without declaring it" ) @Test void canThrowChecked() { class FlameThrower { void throwExceptionDeclared() throws Exception { throw new Exception(); } void throwExceptionSecretly() { throwSneaky( new Exception()); } } final var sut = new FlameThrower(); assertThat(catchThrowable(() -> sut.throwExceptionDeclared())).isInstanceOf(Exception. class ); assertThat(catchThrowable(() -> sut.throwExceptionSecretly())).isInstanceOf(Exception. class ); } int doesNotReturn(){ throw throwSneaky( new Exception()); // no need for a return command } }The two methods throwExceptionDeclared() and throwExceptionSecretly() demonstrate the difference between normal and sneaky throwing.
The method throwSneaky() never returns, and it still has a declared return value. The reason for that is to allow the pattern that can be seen in the method doesNotReturn() towards the end of the text code. We know that the method throwSneaky() never returns, but the compiler does not know. If we simply call it then the compiler will still require some return statement in our method. In more complex code flow it may complain about uninitialized variables. On the other hand if we “throw” the return value in the code then it gives the compiler a hint about the execution flow. The actual throwing on this level will never happen actually, but it does not matter.
Never catch Throwable , ...Error or COVID
When we catch an exception we can catch checked exception, RuntimeException or just anything that is Throwable . However, there are other things that are Throwable but are not exceptions and are also not checked. These are errors.
故事:
I do a lot of technical interviews where candidates come and answer my questions. I have a lot of reservations and bad feelings about this. I do not like to play “God”. On the other hand, I enjoy a lot when I meet clever people, even if they are not fit for a given work position. I usually try to conduct the interviews that the value from it is not only the evaluation of the candidate but also something that the candidate can learn about Java, the profession, or just about themselves. There is a coding task that can be solved using a loop, but it lures inexperienced developers to have a solution that is recursive. Many of the developers who create the recursive solution realize that there is no exit condition in their code for some type of the input parameters. (Unless there is because they do it in the clever way. However, when they are experienced enough, they do not go for the recursive solution instead of a simple loop. So when it is a recursive solution they almost never have an exit condition.) What will happen if we run that code with an input parameter that never ends the recursive loop? We get a StackOverflowException . Under the pressure and stress of the interview, many of them craft some code that catches this exception. This is problematic. This is a trap!
Why is it a trap? Because the code will not ever throw a StackOverflowException . There is no such thing in the JDK as StackOverflowException . It is StackOverflowError . It is not an exception, and the rule is that
YOUR CODE MUST NEVER CATCH AN ERROR
The StackOverflowError (not exception) extends the class VirtualMachineError which says in the JavaDoc:
Thrown to indicate that the Java Virtual Machine is broken
When something is broken you can glue it together, mend, fix, but you can never make it unbroken. If you catch a Throwable which is also an instance of Error then the code executing in the catch part is run in a broken VM. What can happen there? Anything and the continuation of the execution may not be reliable.
Never catch an Error !
摘要和總結(jié)
In this article we discussed exceptions, specifically:
- how to throw meaningful exceptions by adding information when it becomes available,
- how to replace the stack trace of an exception with setTrackTrace() when it makes sense,
- how to collect exceptions with addSuppressed() when your application can throw exceptions multiple times We also discussed some interesting bits about how the JVM does not know about checked exceptions and why you should never catch an Error .
Don't just (re)throw exceptions when they happen. Think about why and how they happen and handle them appropriately.
Use the information in this article to make your code exceptional 😉
(Code and article were reviewed and proofread by Mihaly Verhas. He also wrote the takeaway section including the last
sentence.)
翻譯自: https://www.javacodegeeks.com/2020/05/all-you-wanted-to-know-about-throwable.html
總結(jié)
以上是生活随笔為你收集整理的您想了解的所有Throwable的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自然数 素数 质数_在Java中获取素数
- 下一篇: 石化区备案介质整改报告(石化区备案)