12.4java
以前程序裏寫日誌不清楚怎麼把printStackTrace()輸出的內容寫到日誌裏,僅僅是寫getMessage()信息少了很多。在本節的例子中給出了一個方法:程序員
StringWriter sw = new StringWriter();編程
PrintWriter pw = new PrintWriter(sw);數組
e.printStrackTrace(pw);函數
logger.error(sw.toString());測試
12.5編碼
能夠聲明方法將異常拋出,但實際上該方法並不拋出異常。這樣作的好處是爲異常先佔一個位子,之後能夠拋出這種異常而不用修改方法聲明。spa
在編譯時被強制檢查的異常稱爲被檢查的異常。.net
練習8題中說「拋出練習3裏定義的異常」錯了,應該是練習4中定義的異常。中有個細節須要注意:聲明方法時標識了拋出的異常,即便方法內部並無真正拋出此異常,調用該方法的時仍是要處理異常,不然編譯器會報錯。日誌
12.6
Exception是與編程有關的全部異常類的基類。它從Throwable類中繼承了一些方法:
String getMessage()
String getLocalizedMessage()
獲取異常信息和用本地語言表示的異常信息。
void printStackTrace()
void printStackTrace(PrintStream)
void printStackTrace(PrintWriter)
打印信息和調用棧軌跡。第一個版本輸出到標準錯誤流,後兩個版本能夠選擇要輸出的流。
Throwable fillInStrackTrace()這個方法的做用是對調用的對象從新填充調用棧,使得調用棧看起來和新建立的異常是相同的。通過編碼測試,方法返回的Throwable的對象和調用對象是同一個對象,看來只是改變了調用棧的數據,如下是測試代碼:
public class FilInStackTraceTest { public void f() throws Exception { throw new Exception(); } public void g() { try { f(); } catch (Exception e) { e.printStackTrace(); Exception e1 = (Exception)e.fillInStackTrace(); System.out.println(e1 == e); e.printStackTrace(); e1.printStackTrace(); } } public static void main(String[] args) { FilInStackTraceTest test = new FilInStackTraceTest(); test.g(); } }
輸出結果以下:
java.lang.Exception
at com.sfauto.exception.FilInStackTraceTest.f(FilInStackTraceTest.java:6)
at com.sfauto.exception.FilInStackTraceTest.g(FilInStackTraceTest.java:11)
at com.sfauto.exception.FilInStackTraceTest.main(FilInStackTraceTest.java:23)
java.lang.Exception
at com.sfauto.exception.FilInStackTraceTest.g(FilInStackTraceTest.java:14)
at com.sfauto.exception.FilInStackTraceTest.main(FilInStackTraceTest.java:23)
java.lang.Exception
at com.sfauto.exception.FilInStackTraceTest.g(FilInStackTraceTest.java:14)
at com.sfauto.exception.FilInStackTraceTest.main(FilInStackTraceTest.java:23)
true
從最後一行能夠看出,e與e1是一個對象,調用fillInStackTrace()方法以前和以後e.printStackTrace()的打印結果不一樣,而調用filllInStackTrace()方法以後e與e1printStackTrace()的打印同。因而可知fillInStackTrace()是改變了調用對象的調用棧。
printStackTrace()方法所提供的信息能夠經過getStackTrace()方法來獲取,此方法將返回一個數組,每個元素都表示棧中的一幀。元素0是棧頂元素,是調用序列中的最後一個方法調用(即Throwable被建立和拋出之處,離異常最近的方法),數組中最後一個元素是棧底元素,即調用的最外層方法。
經常會想要在捕獲一個異常後拋出另外一個異常,而且但願把原始異常的信息保存下來,這被稱爲異常鏈。Throwable和它的一些子類提供了帶有參數Throwable cause的構造函數來維持異常鏈。書中說只有Error、Exception和RuntimeException提供這個構造函數,好像說的不對,至少我知道的SQLException和IOException都有這種構造函數。在沒有此種構造函數的狀況下能夠調用initCause()方法來達到相同的效果。
12.7
Throwable這個Java類被用來表示任何能夠做爲異常被拋出的類。Throwable對象能夠分爲兩種類型:Error用來表示編譯時錯誤和系統錯誤,除特殊狀況外不用關心;Exception是與編程打交道的基本異常類型,在Java類庫、用戶方法以及運行時故障中均可能拋出Exception異常。因此Java程序員關心的基本類型一般是Exception。
異常的基本概念是用名稱表明發生的問題,異常的名稱能夠望文生義。異常並不是全在java.lang包中,還存在於util、net和io包中。
有一些問題屬於Java的標準運行時檢測的一部分,它們會自動被Java虛擬機拋出,因此沒必要在方法的異常說明中把它們列出來,這樣的異常被稱爲不受檢查的異常(也有叫運行時異常的吧),它們都是RuntimeException的子類。這種異常屬於錯誤,不強制要求手動捕獲,能夠再本身的代碼中拋出這種異常。若是不捕獲這種異常,它會穿越全部的執行路徑直達main()方法,在主程序退出前將調用異常的printStackTrace()方法。
RuntimeException表明的是編程錯誤:
(1)沒法預料的錯誤。好比從你的代碼控制範圍以外傳遞近來的Null引用;
(2)應該在代碼中檢查的錯誤,好比數組越界。
12.8
Java中的異常不容許回到異常拋出的地點,若是想實現這一功能能夠把try快放到循環中,這就創建了一個「程序繼續執行以前必需要達到」的條件,還能夠加入一個static類型的計數器或者別的裝置,使循環在放棄之前能嘗試必定的次數。
當涉及到break和continue語句的時候,finally子句也會獲得執行。
當try塊中包含return語句,其後的finally塊也會被執行。
12.8.3小節中做者演示了兩種不恰當的方法使得一些異常被忽略,值得注意
第一種問題的解決方法
12.9
當覆蓋方法時,只能拋出在基類方法的異常說明中列出的異常,能夠少拋出或不拋出這些異常,也能夠拋出這些異常的子類,或者不拋出異常。可是不能添加新的異常。即某個方法的異常說明範圍能夠變小可是不能變大。
異常限制對構造器不起做用。子類的構造器能夠拋出任何新異常。可是由於基類構造器必須以這樣或那樣的方法被調用,派生類構造函數的異常說明必須包含基類構造函數的異常說明。
派生類構造函數不能捕獲基類構造函數拋出的異常。
12.10
對於拋出異常的構造函數,做者認爲應該這樣處理:在一個單獨try-catch語句中構造對象,一旦對象構形成功(即構造函數未拋出異常)用另外的嵌套try-catch-finally語句寫其餘功能,在finally中記得清理該對象的資源。
12.11
練習25中,子類覆蓋了父類的方法而且拋出了比父類方法更窄的異常,當咱們建立了一個子類的對象,並將其向上轉型爲父類,調用該方法編譯器會強制要求捕獲父類的異常。
12.12
對於一些不知道怎麼處理的被檢查異常,做者推薦兩種辦法:
(1)在main()函數中拋出這些異常;
(2)利用異常鏈,把被檢查異常包裝成RuntimeException拋出
throw new RuntimeException(e);
補充(前兩條來自《Java 8編程參考官方教程》,第三條官方教程看不明白,參考瞭如下博客http://blog.csdn.net/jackiehff/article/details/17839225,以前的例子理解了,最後的論述依舊不是很懂)
1.7版本,異常系統添加的三個新特性:
一、帶資源的try
這種特性有時被稱爲自動資源管理(Automatic Resource Management,ARM)
try語句的形式:
try(資源定義和初始化) {
}
在資源定義和初始化語句中聲明的變量當try語句塊結束時,自動釋放資源。只有實現了AutoCloseable接口的資源才能使用帶資源的try語句,該接口定義了close()方法,try語句塊結束的時候會調用資源的close()方法。
try語句中聲明的資源被隱式的聲明爲fianl,這意味着在建立資源變量後不能將其餘變量賦值給該引用。另外,資源的做用域侷限於帶資源的try語句。
能夠再一條try語句中管理多個資源。爲此,只須要簡單的使用分號分隔每一個資源便可。
關閉資源的close()方法也可能拋出異常,使用帶資源的try語句時,當try語句塊中拋出異常同時close()方法也拋出異常,close()方法拋出的異常會被抑制,但它並無丟失,而是被添加到第一個異常的抑制列表中,使用Throwable類定義的getSupperessed()方法能夠獲取抑制異常列表。
二、多重捕獲
容許經過相同的catch子句捕獲多個異常,使用操做符 | 分隔每一個異常。每一個多重捕獲參數都被隱式的聲明爲final,所以不能賦予它新的值。正常的捕獲並無final的限制,我想是由於多重捕獲形式以下:
catch(IOException | ArrayIndexOutOfBoundsException e ) {
}
若是要在catch語句塊中從新賦值,編譯器搞不清楚e究竟是第一個類型仍是第二個類型。
三、從新拋出精確的異常
考慮下面的例子:
static class FirstException extends Exception { } static class SecondException extends Exception { } public void rethrowException(String exceptionName) throws Exception { try { if (exceptionName.equals("First")) { throw new FirstException(); } else { throw new SecondException(); } } catch (Exception e) { throw e; } }
這個例子的 try
塊既能夠拋出 FirstException
也能夠拋出 SecondException
。假定你想要在rethrowException方法聲明中的 throws
子句中指定異常類型爲這兩種類型,在Java SE 7以前的版本中你不能這麼作。由於 catch
子句的異常參數e是 Exception
類型,而且 catch塊從新拋出這個異常參數 e,因此你只能在rethrowException方法聲明中的 throws
子句中指定異常類型爲 Exception
。
然而在Java SE 7中, 你能夠在rethrowException方法聲明中的 throws
子句中指定異常類型爲 FirstException
和 SecondException
。 Java SE 7編譯器能夠探測到由 throw e
語句拋出的異常必須來自於 try
塊, 而且 try
塊拋出的異常只能是 FirstException
和 SecondException
。即便 catch
子句的異常參數e的類型是 Exception
,編譯器也能夠探測到它是 FirstException
仍是 SecondException
的實例:
public void rethrowException(String exceptionName) throws FirstException, SecondException { try { // ... } catch (Exception e) { throw e; } }
若是 catch
塊中的 catch
參數被指定給另外一個值,那麼這種分析失效。然而,若是的 catch
參數被指定給另外一個值, 你必須在方法聲明的 throws
子句中指定異常類型爲 Exception
。
具體說來,在Java SE 7及後續版本中, 當你在一個 catch
子句中聲明一個或多個異常類型而且從新拋出由這個 catch
塊處理的異常,編譯器會驗證從新拋出的異常類型是否知足如下條件:
try
塊能夠拋出它。catch
塊沒有辦法處理它。catch
子句其中一個異常參數的子類或者超類。