Java基础 — 异常
異常對象都是派生于Throwable 類的一個實例。
異常層次結構簡化示意圖:
所有的異常都是由Throwable 繼承而來,但在下一層立即分解為兩個分支:Error 和 Exception
Error類層次結構描述了Java運行時系統的內部錯誤和資源耗盡錯誤
Exception 層次
Exception 層次分解為兩個分支: RuntimeException 和 其他異常
RuntimeException 異常:
不是派生于 RuntimeException 異常包括:
Java語言規范將派生于 Error 異常或 RuntimeException 類的所有異常稱為非受查(unchecked) 異常。
所有其他的異常稱為受查(checked)異常。
什么時候該拋出異常 throws :
子類方法中聲明的受查異常并不能比超類方法中聲明的異常更通用,即子類方法中可拋出更特定的異常,或者根本不拋出任何異常。特別聲明:如果超類方法沒有拋出任何受查異常,子類也不能拋出任何受查異常。
自定義異常:
API java.lang.Throwable
- Throwable() 構造一個新的Throwable 對象,這個對象沒有詳細的描述信息
- Throwable(String message) 構造一個新的Throwable,這個對象帶有特定的詳細描述信息。習慣上,所有的派生的異常類都支持一個默認的構造器和一個帶有詳細信息的構造器。
- String getMessage() 獲得Throwable 對象的詳細描述信息
異常處理小技巧
一般異常處理最好的選擇,就是將異常傳遞給調用者,讓調用者自己去操心。
在catch 字句中可以拋出一個異常,這樣做的目的是改變異常的類型。我們可以采用一種比較推薦的處理異常的方法,并且將原始異常設置為新異常的"原因":
try {access the database } catch(SQLException e) {Throwable se = new ServletException("database error");se.initCause(e);throw se; } 復制代碼當捕獲到異常時,就可以使用下面這條語句重新得到原始異常:
Throwable e = se.getCause(); 復制代碼使用這種包裝技術,可讓用戶拋出子系統中的高級異常,而不會丟失原始異常的細節
如果在一個方法中發生了一個受查異常,而不允許拋出它,那包裝技術就十分有用。我們可捕獲這個受查異常,并將它包裝成一個運行時異常。
finally 語句
不管是否有異常被捕獲,finally 字句中的代碼都被執行。
當finally字句包含return 語句時,將會出現一種意想不到的結果。
假設利用return 語句從try語句塊中退出。在方法返回前,finally字句的內容將被執行。如果finally字句中也有一個return語句,這個返回值將會覆蓋原始的返回值。例:
public static int f(int n) {try{return n*n;}finally {if (2 == n)return 0;} } 復制代碼如果調用f(2) ,try語句返回結果為4,然而在方法返回前,要執行finally字句。finally字句使得方法返回0。這個返回值覆蓋了原先的返回值4。所以調用 f(2) 返回的值為 0。
JAVA SE7 關閉資源的處理
待資源的try 語句(try-with-resources) 的最簡形式
try(Resource res = ...) {work with res } 復制代碼try 塊退出時,會自動調用res.close()
指定多個資源:
try(Scanner in = new Scanner(new FileInputStream("/usr/shar/dict/words"),"UTF-8");PrintWriter out = new PrintWriter("out.txt")){while (in.hasNext())out.println(in.next().toUpperCase()); } 復制代碼不管這個塊如何退出,in 和 out 都會關閉。
常規方式手動編程,就需要兩個嵌套的try/finally 語句。
堆棧軌跡(stack trace)
堆棧軌跡是一個方法調用過程的列表,它包含了程序執行過程中方法調用的特定位置。
訪問堆棧軌跡的文本描述信息
Throwable t = new Throwable(); StackTraceElement[] frames = t.getStackTrace(); for (StackTraceElement frame : frames){analyze frame } 復制代碼StackTraceElement 類含有能夠獲得文件名和當前執行的代碼行號的方法。同時,還含有能夠獲得類名和方法名的方法。
靜態的 Thread.getAllStackTrace 方法,它可以產生所有線程的堆棧軌跡。例
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces(); for (Thread t : map.keySet()){StackTraceElement[] frames = map.get(t);analyze frames } 復制代碼打印一個遞歸階乘的函數的堆棧情況
public class StackTraceTest {/*** 計算n的階乘* @param n* @return*/public static int factorial(int n){System.out.println("factorial(" + n + "):");Throwable t = new Throwable();StackTraceElement[] frames = t.getStackTrace();for (StackTraceElement f: frames)System.out.println(f);int r;if (n<=1)r =1;elser = n * factorial(n-1);System.out.println("return " + r);return r;}public static void main(String[] args){Scanner in = new Scanner(System.in);System.out.print("Enter n : ");int n = in.nextInt();factorial(n);} } 復制代碼計算factorial(3),打印一下內容
factorial(3): javabook.StackTraceTest.factorial(StackTraceTest.java:15) javabook.StackTraceTest.main(StackTraceTest.java:32) factorial(2): javabook.StackTraceTest.factorial(StackTraceTest.java:15) javabook.StackTraceTest.factorial(StackTraceTest.java:23) javabook.StackTraceTest.main(StackTraceTest.java:32) factorial(1): javabook.StackTraceTest.factorial(StackTraceTest.java:15) javabook.StackTraceTest.factorial(StackTraceTest.java:23) javabook.StackTraceTest.factorial(StackTraceTest.java:23) javabook.StackTraceTest.main(StackTraceTest.java:32) return 1 return 2 return 6 復制代碼使用異常小技巧
異常處理不能代替簡單的測試
與執行簡單的測試相比,捕獲異常所花費的時間大大超過前者。因此使用異常的基本規則是,旨在異常情況下使用異常機制。
不要過分地細化異常
將整個任務包裝在一個try塊中,這樣,當任何一個操作出現問題時,整個任務都可以取消。
利用異常層次結構
- 不要只拋出 RuntimeException 異常。應該尋找更加適當的子類或創建自己的異常類。
- 不要只捕獲Throwable 異常,否則,會使程序代碼更難讀、更難維護
- 考慮受查異常和非受查異常的區別。
- 將一種異常轉換成另一種更加適合的異常時不要猶豫。
不要壓制異常
在java中,往往強化地傾向關閉異常。
在檢測錯誤時,“苛刻”要比放任更好
例如,當棧空時,Stack.pop 是要返回一個null,還是拋出一個異常?我們認為:在出錯的地方拋出一個 EmptyStackException異常要比在后面拋出一個 NullPointerException 異常更好。
不要羞于傳遞異常
讓高層次的方法通知用戶發生了錯誤,或者放棄不成功的命令更加適宜。
5 和 6 可以歸納為“早拋出,晚捕獲”。
參考
JAVA核心技術(卷1)原書第10版
總結
以上是生活随笔為你收集整理的Java基础 — 异常的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 51nod 正整数分组
- 下一篇: caioj 1066 动态规划入门(一维