被抑制的異常在新的Java 7 try-with-resources語句 (也稱為自動資源管理 [ ARM ])的執行中起著重要作用。 為這種新的資源管理功能提供API支持似乎是Throwable類上新的構造函數和方法的主要驅動程序,它們提供對抑制的異常的訪問,但是API支持在try-with-resources語句之外使用抑制的異常。 。
遇到受抑制的異常的最常見用例是,當try-with-resources語句(這是一種嘗試類型,根據Java語言規范Java SE 7 Edition 14.20 ,它不需要catch或finally子句時)遇到一個異常。 try塊中的異常,然后在隱式嘗試關閉相關資源時遇到另一個異常。
為了說明這種情況,我組成了自己的“資源”,可以在try-with-resource語句中使用它,因為它實現了java.lang.AutoCloseable接口。 當使用它的代碼嘗試使用它時,此“頑皮資源”會故意引發異常,然后在調用重寫的close方法(由AutoCloseable接口指定的唯一方法)時引發另一個異常,從而繼續其不良形式。 接下來顯示NaughtyResource的代碼清單。
NaughtyResource.java
package dustin.examples;/*** Resource that throws exceptions both in its use and its closure and is only* intended for use in demonstrating Java 7's suppressed exceptions APIs. This* is not a well-behaved class.* * @author Dustin*/
public class NaughtyResource implements AutoCloseable
{/*** Method that intentionally throws an exception.* * @throws RuntimeException Thrown no matter how you call me.*/public void doNothingGood(){throw new RuntimeException("Nothing good can come of this.");}/*** The overridden closure method from AutoCloseable interface.* * @throws Exception Exception that might be thrown during closure of this* resource.*/@Overridepublic void close() throws Exception{throw new UnsupportedOperationException("Not supported yet.");}
}
現在有了頑皮的資源,是時候使用頑皮的資源并演示抑制異常API。 下一張圖片描述了如果嘗試使用此資源而不捕獲close方法隱式拋出的Exception且未聲明該方法拋出該異常的情況,將發生什么情況。

這是javac提供的錯誤消息,如以下屏幕快照所示。

我已經顯示了前兩個屏幕快照,以強調對資源執行的隱式關閉調用。 NetBeans編輯器和控制臺中顯示的錯誤消息清楚地表明了這種情況。
下一個代碼清單包含編譯的SuppressedExceptions類的第一個版本。
SuppressedExceptions.java(版本1)
package dustin.examples;/*** Demonstrate JDK 7's Suppressed Exceptions API support.* * @author Dustin*/
public class SuppressedExceptions
{/*** Executable function demonstrating suppressed exceptions.* * @param arguments The command line arguments; none expected.*/public static void main(String[] arguments) throws Exception{try (NaughtyResource naughty = new NaughtyResource()){naughty.doNothingGood();}}
}
盡管在執行上述代碼時確實遇到了兩個異常(一個在NaughtyResource.doNothingGood()調用的try塊中,一個在隱式調用close方法的調用中),但只有一個滲透到頂部,并在應用程序顯示時顯示運行。 下一個屏幕快照對此進行了演示。

如最后一個圖像所示,僅顯示在try-with-resources語句的try try塊中遇到的異常。 在這里,可以向Throwable(在這種情況下為Exception)詢問其受抑制的異常的功能非常有用。 為了證明這一點,接下來顯示SuppressedExceptions類的版本2(使用Throwable.getSuppressed() )。
SuppressedExceptions.java(版本2)
package dustin.examples;import static java.lang.System.err;/*** Demonstrate JDK 7's Suppressed Exceptions API support.* * @author Dustin*/
public class SuppressedExceptions
{/*** Method that uses NaughtyResource with try-with-resource statement.* * @throws Exception Expected exception for try-with-resource used on the* NaughtyResource.*/public static void performTryWithResource() throws Exception{try (NaughtyResource naughty = new NaughtyResource()){naughty.doNothingGood();} }/*** Executable function demonstrating suppressed exceptions.* * @param arguments The command line arguments; none expected.*/public static void main(String[] arguments){try{performTryWithResource();}catch (Exception ex){err.println("Exception encountered: " + ex.toString());final Throwable[] suppressedExceptions = ex.getSuppressed();final int numSuppressed = suppressedExceptions.length;if (numSuppressed > 0){err.println("\tThere are " + numSuppressed + " suppressed exceptions:");for (final Throwable exception : suppressedExceptions){err.println("\t\t" + exception.toString());}}}}
}
上面的應用程序將打印出所有抑制的異常。 在這種情況下,嘗試關閉NaughtyResource時遇到的異常現在顯示為被抑制的異常之一。 下一個屏幕快照對此進行了演示。

如Java教程中所述,我們看到唯一拋出的異常是在try-with-resources語句的try塊中遇到的異常,并且在資源關閉期間遇到的第二個異常被“抑制”(但仍可從實際拋出異常)。
不需要使用try-with-resources來處理抑制的異常。 下一個代碼清單將SuppressedExceptions改編為使用更傳統的try-finally。
SuppressedExceptions.java(版本3)
package dustin.examples;import static java.lang.System.err;/*** Demonstrate JDK 7's Suppressed Exceptions API support.* * @author Dustin*/
public class SuppressedExceptions
{/*** Method that uses NaughtyResource with JDK 7 try-with-resource statement.* * @throws Exception Expected exception for try-with-resource used on the* NaughtyResource.*/public static void performTryWithResource() throws Exception{try (NaughtyResource naughty = new NaughtyResource()){naughty.doNothingGood();} }/*** Method that uses NaughtyResource with traditional try-finally statement.* * @throws Exception Exception thrown during use of NaughtyResource.*/public static void performTryFinally() throws Exception{final NaughtyResource naughty = new NaughtyResource();try{naughty.doNothingGood();}finally{naughty.close();}}/*** Executable function demonstrating suppressed exceptions.* * @param arguments The command line arguments; none expected.*/public static void main(String[] arguments){try{
// performTryWithResource();performTryFinally();}catch (Exception ex){err.println("Exception encountered: " + ex.toString());final Throwable[] suppressedExceptions = ex.getSuppressed();final int numSuppressed = suppressedExceptions.length;if (numSuppressed > 0){err.println("\tThere are " + numSuppressed + " suppressed exceptions:");for (final Throwable exception : suppressedExceptions){err.println("\t\t" + exception.toString());}}else{err.println("\tNo Suppressed Exceptions.");}}}
}
上面的代碼調用了一種使用try-finally的方法,其行為與try-with-resources示例的行為不同,如下面的屏幕快照所示。

try-finally顯示嘗試關閉資源時遇到的異常,而不是使用資源時遇到的異常(上面顯示的try-with-resources相反)。 另一個不同之處是,即使在try-finally情況下,作為抑制的異常,其他異常也不可用。 這是一個廣為宣傳的 “ 丟失的異常 ”問題的示例 ,該問題可能包含潛在的“瑣碎”異常(關閉資源),而隱藏了潛在的更重要的異常(實際上是在使用資源)。
如果我想同時查看NaughtyResource的使用和關閉過程中引發的異常以及try-finally,可以使用Throwable.addSuppressed(Throwable) ,如下面的代碼清單所示。 在該清單中,對try和finally子句進行了增強,以捕獲使用NaughtyResource期間引發的異常,并將捕獲的異常添加到實際拋出的異常中作為抑制的異常。
SuppressedExceptions.java(版本4)
package dustin.examples;import static java.lang.System.err;/*** Demonstrate JDK 7's Suppressed Exceptions API support.* * @author Dustin*/
public class SuppressedExceptions
{/*** Method that uses NaughtyResource with JDK 7 try-with-resource statement.* * @throws Exception Expected exception for try-with-resource used on the* NaughtyResource.*/public static void performTryWithResource() throws Exception{try (NaughtyResource naughty = new NaughtyResource()){naughty.doNothingGood();} }/*** Method that uses NaughtyResource with traditional try-finally statement.* * @throws Exception Exception thrown during use of NaughtyResource.*/public static void performTryFinally() throws Exception{final NaughtyResource naughty = new NaughtyResource();Throwable throwable = null;try{naughty.doNothingGood();}catch (Exception usingEx){throwable = usingEx;}finally{try{naughty.close();}catch (Exception closingEx){if (throwable != null){closingEx.addSuppressed(throwable);throw closingEx;}}}}/*** Executable function demonstrating suppressed exceptions.* * @param arguments The command line arguments; none expected.*/public static void main(String[] arguments){try{
// performTryWithResource();performTryFinally();}catch (Exception ex){err.println("Exception encountered: " + ex.toString());final Throwable[] suppressedExceptions = ex.getSuppressed();final int numSuppressed = suppressedExceptions.length;if (numSuppressed > 0){err.println("\tThere are " + numSuppressed + " suppressed exceptions:");for (final Throwable exception : suppressedExceptions){err.println("\t\t" + exception.toString());}}else{err.println("\tNo Suppressed Exceptions.");}}}
}
修改后的類的輸出如下所示。 注意,由于使用資源本身而導致的異常現在與作為抑制的異常而引發的異常相關聯。 盡管此處未顯示,但Throwable現在還提供了一個構造函數,該構造函數允許通過布爾參數指定新實例化的Throwable是否允許(啟用)抑制的異常(禁止)。

查看抑制異常的另一個角度是完整堆棧跟蹤。 以下三幅圖像是使用try-with-resources(顯式顯示假定的異常),使用傳統的try-finally語句而未顯式添加抑制的異常(因此在full stack跟蹤中沒有抑制的異常)導致的完整堆棧跟蹤的屏幕快照。 ,并使用傳統的try-finally方法,并明確添加抑制的異常(因此確實會出現在完整堆棧跟蹤中)。 我在每個圖像上都添加了一條紅線,以單獨顯示整個堆棧跟蹤結束的位置,并在適用的情況下,圈出了對抑制異常的顯式引用。



抑制的異常與鏈接的異常
禁止的異常與鏈接的異常不同 。 鏈接異常是JDK 1.4引入的,旨在使輕松跟蹤異常之間的因果關系成為可能。 通常,鏈接的異常是由于將新引發的異常與已捕獲并導致引發新異常的異常相關聯而導致的。 例如,可能會引發未檢查的異常,從而“包裝”已捕獲的已檢查的異常,并且可以將它們鏈接在一起。
JDK 7引入了抑制的異常,它與因果關系無關,而與在單個拋出的異常中表示可能相關但不一定因果的多個例外條件有關。 在上面的我的頑皮資源示例中,頑皮資源在其唯一方法被調用時的異常并不是導致它在調用其close方法時引發異常的原因。 因此,將兩個異常關聯起來(通過抑制的異常機制)比強迫一個異常似乎是造成連鎖關系中另一個異常的原因更有意義。
通過比較Throwable訪問每種類型的方法,最快速地了解鏈式異常與抑制型異常之間的區別可能是最容易的。 對于鏈式異常,將調用特定的Exception(Throwable)方法。 此方法返回導致Throwable的單個實例。 可以詢問返回的Throwable的原因,并在導致Throwables的整個過程中重復該過程。 重要的觀察結果是,每個給定的Throwable都有一個導致Throwable的原因。 這可以與Throwable在調用其getSuppressed()方法時提供多個抑制的Throwable(在數組中)的能力進行對比。
NetBeans推薦嘗試資源
在此值得注意的是,NetBeans會警告您嘗試使用try-finally,并建議使用try-with-resources替換它,如我在我的文章《 用于現代化Java代碼的七個NetBeans提示》和下面顯示的屏幕快照中所討論的。

結論
我相信很明顯,為什么NetBeans建議開發人員將對資源處理的最后嘗試更改為try-with-resources語句。 通常最好先了解資源上的哪個操作導致了異常,但是如果需要的話,也能夠在關閉時訪問異常是很不錯的。 如果必須選擇,我通常會對執行期間的資源問題更感興趣,因為關閉問題可能是該問題的衍生形式。 但是,兩者都更好。 傳統的try-finally最終僅列出關閉時的異常,而無需付出額外的努力來中繼這兩者。 try-with-resource語句不僅更簡潔; 由于內置支持包含抑制的異常,它也更加有用。
參考: JCG合作伙伴 Dustin Marx在Inspired by Actual Events博客上提供了Java 7對抑制異常的支持 。
翻譯自: https://www.javacodegeeks.com/2012/04/java-7s-support-for-suppressed.html