2012-09-18 66 views
3

假設我有一個可能因檢查異常而失敗的方法(根據Sun的建議進行檢查,因爲它是可恢復的)。 此方法失敗並觸發恢復策略。 但是最初的方法和恢復策略都失敗了。當第一策略和恢復策略都失敗時,Java中的異常組合

在某些情況下,我可能要同時擁有蹤跡,讓我知道爲什麼了初始和恢復策略失敗,而不是隻有最後一個。

我該怎麼辦? 我應該創建一個CompositeException類型或類似的東西?這是一個好習慣嗎?

回答

5

爪哇7已經推出了抑制異常的概念。例如,try-with-resources語句是:specified通過:

資源按照與初始化順序相反的順序關閉。僅當資源初始化爲非空值時才關閉資源。關閉一個資源時的異常並不妨礙關閉其他資源。如果之前由初始化程序,try塊或資源關閉引發異常,則會抑制此異常。

  • 如果資源的初始化正常完成,並且try塊完成突然,因爲值V的罰球,那麼:

    • 如果資源的自動關閉由於拋出了一個值V2而突然完成,那麼try-with-resources語句會突然完成,因爲拋出了值V,並將V2添加到抑制的異常列表V.

這使用java.lang.Throwable.addSuppressedException,它的javadoc寫着:

將指定例外,爲了實現這一例外受到打壓的例外。此方法是線程安全的,通常由try-with-resources語句調用(自動和隱式地)。

抑制行爲已啓用,除非通過構造函數禁用。當禁用抑制時,此方法除了驗證其參數外別無其他。

請注意,當一個異常導致另一個異常時,通常會捕獲第一個異常,然後在響應中引發第二個異常。換句話說,這兩個例外之間有因果關係。相反,有些情況下可以在兄弟代碼塊中引發兩個獨立的異常,特別是try-with-resources語句的try塊和編譯器生成的關閉資源的finally塊。在這些情況下,只有一個拋出的異常可以被傳播。在try-with-resources語句中,當有兩個這樣的異常時,源自try塊的異常被傳播,並且finally塊中的異常被添加到由try塊中的異常抑制的異常列表中。由於異常展開堆棧,它可以累積多個抑制異常。

異常可能會抑制異常,同時也是由另一個異常引起的異常。異常是否具有原因在創建時在語義上是已知的,與異常是否會抑制通常僅在拋出異常之後確定的其他異常不同。

請注意,編程人員編寫的代碼也可以利用在存在多個同級異常並且只能傳播一個異常的情況下調用此方法。

最後一段似乎適用於您的情況。所以,你可以這樣做:

public static void main(String[] args) throws Exception { 
    try { 
     throw new RuntimeException("Not now!"); 
    } catch (Exception e) { 
     try { 
      tryAgain(); 
     } catch (Exception e2) { 
      e.addSuppressed(e2); 
      throw e; 
     } 
    } 
} 

然後,堆棧跟蹤將包含兩個異常:

Exception in thread "main" java.lang.RuntimeException: Not now! 
    at tools.Test.main(Test.java:12) 
    Suppressed: java.lang.RuntimeException: I'm on holiday. 
     at tools.Test.tryAgain(Test.java:7) 
     at tools.Test.main(Test.java:15) 

,但只有主異常可以被調用者捕獲。

+0

Neat :) +1。太糟糕了,它只是Java 7。 – Brian

+0

謝謝,正是我需要知道的:) –

+0

如果一個方法被聲明爲'拋出FooException,BarException',是否有任何方法可以明確地禁止清理異常,只有當主線出現異常,而不需要分別捕捉,記錄和重新拋出每種可能的異常? – supercat

0

我會考慮在初始方法失敗的時候恢復策略失敗的異常和其他。

您可能需要做一些像扔異常恢復失敗之前多次嘗試恢復策略(例如,重試,發生超時失敗)。

好像保持分離初始方法和恢​​復策略的例外是有道理的,因爲它們代表了兩種不同的情況。

0

不幸的是,一個Java Exception只有一個原因,所以CompositeException可能是你最好的選擇。但是,您可以覆蓋printStackTrace方法來打印你的組合異常的堆棧跟蹤:

public class CompositeException extends Exception { 
    private final List<Throwable> causes = new ArrayList<Throwable>(); 

    public CompositeException(Throwable... causes) { 
     this.causes.addAll(Arrays.asList(causes)); 
    } 

    // Other constructors you want 

    @Override 
    public void printStackTrace() { 
     if (causes.isEmpty()) { 
      super.printStackTrace(); 
      return; 
     } 
     for (Throwable cause : causes) { 
      cause.printStackTrace(); 
     } 
    } 

    @Override 
    public void printStackTrace(PrintStream s) { 
     if (causes.isEmpty()) { 
      super.printStackTrace(s); 
      return; 
     } 
     for (Throwable cause : causes) { 
      cause.printStackTrace(s); 
     } 
    } 

    @Override 
    public void printStackTrace(PrintWriter s) { 
     if (causes.isEmpty()) { 
      super.printStackTrace(s); 
      return; 
     } 
     for (Throwable cause : causes) { 
      cause.printStackTrace(s); 
     } 
    } 
} 

這是所有你可以做有多個原因的異常在一個例外。要使用它:

CompositeException ce = new CompositeException(ex1, ex2, ex3); 

然後ce.printStackTrace()將打印所有3個異常堆棧跟蹤。

+0

請注意,如果CompositeException被封裝爲另一個異常的原因,將不起作用,因爲包裝器異常的printStackTrace將對CompositeException調用printStackTraceAsCause(它是私有的,因此不能被覆蓋) 。 –

+0

@JesseMerriman沒錯。幸運的是,Java 7抑制了異常。對於使用Java 6或更低版本的用戶,如果異常被封裝,則沒有其他可行的選擇。 – Brian

0

你到底意味着什麼「我可能想要兩個堆棧跟蹤」?我所要做的就是記錄兩者,因爲它們是「不應該發生的事情」。它可以讓你檢查日誌並追蹤出錯的地方。如果你做了「CompositeException」,你很快就會遇到組合性問題:如果「CompositeExceptionHandler」也失敗了會怎麼樣? 我會...

  1. 跟蹤第一個異常,如果可以的話。否則,並引發自定義異常。
  2. 抓住第二個,並採取相應的行動。

這樣你可以提高耦合度。您可以獨立處理這兩個級別