2013-03-26 102 views
4

我很難理解有異常傳播調用堆棧的概念或真正的用處。我知道如何創建它們,但我真的不知道它們什麼時候會用到,比如在一個簡單的真實世界的數學應用程序中。異常向上傳播調用堆棧

public void method1(){ 
    try{ 
     method2(); 
    } 
    catch(Exception e){ 
     e.printStackTrace(); 
    } 
} 

public void method2(){ 
     try{ 
      throw new Exception(); 
     } 
     finally{ 
     System.out.println("no exception, try cleanup"); 
     } 
} 

我得到的,這是基本上它是如何工作的,雖然它可能會更多地參與更多的異常和功能,但我真的不明白在每一個使用這些,而不是僅僅有捕獲點功能。

+1

因爲每個函數中的catch都隱藏了Excecptions **。這是一個常見的「ick代碼」錯誤。如果你不打算處理一個異常,*不要捕捉*。如果你只是想記錄它,而不處理它,*還會重新拋出異常*。 – 2013-03-26 01:47:32

回答

3

...但我沒有真正明白使用這些而不是僅僅捕獲每個函數中的內容。

問題是調用堆棧中的下一個函數可能不知道如何處理異常。例如:

public class Test { 

    public Object doSomething(String source) throws IOException { 
     try (InputStream is = openAsStream(source)) { 
      // ... read and process stuff 
      return ... 
     } 
    } 

    public InputStream openAsStream(String name) throws IOException { 
     String fileName = // ... do something with 'name' 
     return new FileInputStream(name); 
    } 

    public static void main(String[] args) { 
     // ... 
     Test t = new Test(); 
     try { 
      t.doSomething(args[0]); 
     } catch (IOException ex) { 
      System.err.println("Cannot handle '" + args[0] + "'"); 
     } 
    } 
} 

openAsStream調用FileInputStream構造,其可能會引發IOExceptionopenAsStream方法無法從此恢復,因此它可以傳播。 doSomething方法不知道如何處理它,所以它允許它傳播。最後,例外得到main ......它知道如何向用戶解釋問題。現在


你可以寫openAsStreamIOException,打印錯誤信息並返回null。但是,這將是一個很大的錯誤:

  • openAsStream()沒有(和不應該)知道/如何將問題報告給用戶。

  • 如果它向呼叫者返回null,則呼叫者必須測試呼叫的結果是否爲null ...並採取替代操作。

問題是一個方法應該只處理在該級別可以充分處理的異常。其他人應該被允許傳播。 (或者可能包裹在另一個異常中......如果這是API設計所要求的。)

1

有時生成異常的代碼不知道如何正確處理它。如果您處於一段交易代碼中,並且某些事情發生了,那麼該方法/組件可能無法完成的工作不僅僅是在嘗試處理異常時記錄異常。另一方面,一層或多層可能會嘗試重新建立連接,或向請求者提供詳細的錯誤響應。

+0

或「正確死亡」。 – 2013-03-26 01:49:29

+0

我猜我看起來很奇怪的部分是發生異常的函數不能處理異常。 – sl133 2013-03-26 01:50:15

+0

@ sl133想象一下,有一個例外,因爲一個函數 - 比方說,'DbConnection connect(String dbConnStr)'嘗試連接到數據庫,但連接被拒絕。此函數*通常*返回打開的數據庫連接 - 遇到此「特殊情況」時會發生什麼? – 2013-03-26 01:52:19

1

通常調用者具有適當的上下文來處理問題,因爲執行生成異常的操作的代碼沒有。假設我是一個想要將一些數據寫入文件的高級程序。我調用的低級別服務,我們稱之爲writeFile()可能會由於各種原因而拋出IOException。

寫writeFile()的人將不知道將使用writeFile()的上下文。如果writeFile()失敗,是否應該嘗試重寫該文件?應該嘗試多少次?它應該放棄嗎?因爲在完成某些任務的方案中編寫低級函數writeFile()的上下文的程序員在雜草中處於最低程度,因此程序員無法預測調用者想要如何處理錯誤條件。 writeFile()的程序員向調用writeFile()的客戶端指出,存在一些需要的「開放式問題」,而不是試圖猜測調用者如何處理錯誤(一個不可能完成的任務)在出現問題時得到回答。每一個問題都由一個異常類來表示,當客戶程序員捕捉到這個異常時,客戶程序員正在用這個客戶程序員永遠不希望擁有的上下文來回答這個未解決的問題。簡而言之,如果你看到一個列出檢查異常的方法,編寫該異常的程序員就會說「誰調用這個方法將有適當的上下文來決定如何處理這個異常情況。不要。」

+0

那麼writeFile()會拋出IOException,然後再拋出它呢?或者,如果writeFile()有try catch,那麼父類如何捕獲該異常? – sl133 2013-03-26 02:08:53

+0

在Java中,每個方法聲明它可以拋出什麼類型的Checked異常。例如,writeFile()的程序員會寫他的方法聲明void writeFile()拋出IOException {CODE HERE ...},然後在CODE HERE部分的某處,writeFile()的程序員會檢查一些條件IF(FOO == BAR),然後拋出一個異常,如果它不是真的{拋出新的IOException();} – Jazzepi 2013-03-26 08:49:38

+0

因爲writeFile()是非常低的水平,它實際上會創建該異常,然後將其拋出到調用客戶端處理。這是設計。這篇文章其實很不錯。 http://docs.oracle.com/javase/tutorial/essential/exceptions/index.html – Jazzepi 2013-03-26 08:50:53

1

默認情況下,異常傳播使您的代碼在錯誤上快速失敗。

考慮異常傳播的替代方法 - 返回錯誤代碼。如果意外或故意的代碼的調用者沒有測試錯誤代碼,那麼他們可以使用您的方法,而不是意識到您的對象現在處於不可用狀態,並且繼續調用方法並導致未定義的行爲/內存損壞。如果你拋出了一個異常,那麼如果調用者忘記捕捉異常,那麼他們不會做出可怕的事情,而是會失敗得很快,程序員可以被告知它被拋出的位置,爲什麼以及如何處理它。例外是大聲和令人厭惡的,因爲它們表明需要才被考慮的條件。