2008-12-09 61 views
98

在什麼情況下應該在應用程序上捕獲java.lang.Error何時捕獲java.lang.Error?

+0

另請參閱http://stackoverflow.com/questions/2679330/catching-java-lang-outofmemoryerror – Raedwald 2016-04-02 14:41:46

回答

86

一般情況下,從不。 但是,有時您需要捕獲特定的錯誤。

如果你正在編寫framework-ish代碼(加載第三方類),那麼趕上LinkageErrors(沒有找到類def,不滿意的鏈接,不兼容的類改變)可能是明智的。 我也看到了一些愚蠢的第三方代碼拋出錯誤sublcasses,所以你將不得不處理這些。

順便說一下,我不確定無法從OutOfMemory恢復。

6

幾乎從不。錯誤被設計爲應用程序通常無法做到的任何問題。唯一的例外可能是處理錯誤的表示,但即使這樣也可能不會按計劃進行,具體取決於錯誤。

45

從來沒有。您永遠無法確定應用程序是否能夠執行下一行代碼。如果你得到OutOfMemoryError,你有no guarantee that you will be able to do anything reliably。捕獲RuntimeException並檢查異常,但從不錯誤。

http://pmd.sourceforge.net/rules/strictexception.html

+21

永不言敗。我們已經測試了代碼「確定錯誤」;然後捕獲AssertionError以確保設置了-ea標誌。 除此之外...是的,可能永遠不會;-) – 2008-12-09 14:52:43

+2

如何將請求傳遞給工作線程的服務器應用程序。在工作者線程上捕獲Throwable來捕獲任何錯誤並且至少嘗試並記錄出錯的情況可能沒有意義。 – Leigh 2008-12-09 16:11:35

+9

永遠......除非你絕對需要。從來沒有一個強大的詞,總是有例外的規則。如果你正在構建一個框架,即使只是記錄日誌,你也不一定要捕捉和處理某些錯誤。 – Robin 2008-12-09 19:49:56

5

而且有一對夫婦的地方,如果你趕上一個錯誤等情況下,你必須重新拋出。例如ThreadDeath不應該被抓住,它可能會導致大問題是你抓住它在一個封閉的環境(如應用服務器):

應用程序應抓住這一類的實例只有當它必須清理 在異步終止之後。如果ThreadDeath被方法捕獲, 重新執行它是非常重要的,以便線程真正死亡。

+2

這實際上是一個非問題,因爲你只是*不*捕獲`錯誤`。 – Bombe 2008-12-09 14:37:36

7

很少。

我只會說在線程的頂級水平,以便嘗試發出線程死亡原因的消息。

如果你在一個爲你做這種事情的框架,把它留給框架。

6

Error通常不應該被捕獲,因爲它表示應該永遠不會發生異常情況。

從爲Error類的Java API規範:

ErrorThrowable 表示的嚴重問題 合理應用程序不應該試圖 趕上一個子類。大多數此類錯誤是 異常情況。 [...]

不需要在 聲明其throws子句中可能的 執行方法的過程中被拋出,但不是 抓住 錯誤的任何子類的方法,因爲這些錯誤是 不應該 出現的異常情況。

由於規範中提到,一個Error只在那些 機會是,當Error發生時,很少有應用程序可以做的,在某些情況下,Java虛擬機本身可能的情況下拋出處於不穩定的狀態(如VirtualMachineError

雖然ErrorThrowable一個子類,這意味着它可以通過一個try-catch條款被抓住了,但它可能是不是真的需要,因爲應用程序將處於異常在JVM拋出Error時指定狀態。

Java Language Specification, 2nd Edition11.5 The Exception Hierarchy部分中還有關於此主題的簡短部分。

4

非常非常罕見。

我做了它只爲一個非常非常具體的已知案例。 例如,如果兩個獨立ClassLoader加載相同的DLL,java.lang.UnsatisfiedLinkError可能會拋出。 (我同意我應該將JAR移動到共享類加載器)

但最常見的情況是您需要登錄才能知道用戶發出抱怨時發生了什麼。你需要一條消息或一個彈出窗口給用戶,而不是默默地死去。即使是在C/C++中的程序員,他們也會彈出一個錯誤信息,並告訴人們在退出之前不理解的內容(例如內存失敗)。

6

如果你足夠瘋狂地創建一個新的單元測試框架,你的測試運行器可能需要捕獲任何測試用例拋出的java.lang.AssertionError。

否則,請參閱其他答案。

16

一般而言,您應該始終抓住java.lang.Error並將其寫入日誌或將其顯示給用戶。我在支持中工作,每天都會看到程序員無法分辨程序中發生了什麼。

如果你有一個守護線程,那麼你必須防止它被終止。在其他情況下你的應用程序將正常工作。

您應該只在最高級別捕獲java.lang.Error

如果你看看錯誤列表,你會發現大部分都可以被處理。例如,在讀取損壞的zip文件時發生ZipError

最常見的錯誤是OutOfMemoryErrorNoClassDefFoundError,它們在大多數情況下都是運行時問題。

例如:

int length = Integer.parseInt(xyz); 
byte[] buffer = new byte[length]; 

可以產生OutOfMemoryError,但它是一個運行時的問題,沒有任何理由終止你的程序。

NoClassDefFoundError如果庫不存在或者您使用另一個Java版本,則會發生這種情況。如果它是您的程序的可選部分,那麼您不應該終止您的程序。

我可以給出更多的例子,說明爲什麼在頂層捕獲Throwable併產生有用的錯誤消息是一個好主意。

13

在多線程環境中,你最想要抓住它!當你抓住它,記錄它,並終止整個應用程序!如果你不這樣做,那麼可能會做一些關鍵部分的某個線程就會死機,其他應用程序會認爲一切正常。除此之外,許多不需要的情況都可能發生。 一個最小的問題是,如果其他線程由於一個線程不工作而開始拋出一些異常,您將無法輕鬆找到問題的根源。

例如,通常循環應該是:

try { 
    while (shouldRun()) { 
     doSomething(); 
    } 
} 
catch (Throwable t) { 
    log(t); 
    stop(); 
    System.exit(1); 
} 

甚至在某些情況下,你會希望以不同的方式處理不同的錯誤,例如,在OutOfMemoryError異常你就可以定期關閉應用程序(甚至可能免費一些記憶,並繼續),在其他一些情況下,你可以做的事情並不多。

1

理想情況下,我們不應該在我們的Java應用程序中捕獲錯誤,因爲它是一種異常情況。應用程序將處於異常狀態,並可能導致清理或給出一些嚴重錯誤的結果。

1

在單元測試中發現檢查斷言的錯誤可能是適當的。如果某人禁用斷言或以其他方式刪除您想要知道的斷言

3

在Android應用程序中,我正在捕獲java.lang.VerifyError。我正在使用的庫不適用於具有舊版本操作系統的設備,並且庫代碼會拋出此類錯誤。當然我能避免錯誤在運行時檢查操作系統的版本,但:

  • 最早支持的SDK可能會在未來的特定庫改變
  • 在try-catch錯誤塊中更大的一部分回落機制。一些特定的設備,雖然它們應該支持圖書館,但是會引發例外。我捕獲了VerifyError和所有異常以使用回退解決方案。
1

當JVM沒有像預期的那樣工作或處於邊緣時,出現錯誤。如果你發現一個錯誤,不能保證catch塊會運行,甚至更少的運行直到結束。

它也將取決於正在運行的計算機,當前的內存狀態,所以沒有辦法測試,盡力而爲。你只會有一個糟糕的結果。

您還將降級您的代碼的可讀性。

3

在測試環境中捕獲java.lang.AssertionError非常方便...

2

理想情況下,我們不應該處理/捕獲錯誤。但根據框架或應用程序的要求,可能會出現我們需要做的情況。假設我有一個XML解析器守護進程,它實現了DOM解析器,它消耗更多的內存。如果有像Parser這樣的需求線程在得到OutOfMemoryError時不應該死掉,而應該處理它併發送消息/郵件給應用程序/框架的管理員。