:notebook: 本文已歸檔到:「blog」html
:keyboard: 本文中的示例代碼已歸檔到:「javacore」java
Throwable
是 Java 語言中全部錯誤(Error
)和異常(Exception
)的超類。git
Throwable
包含了其線程建立時線程執行堆棧的快照,它提供了 printStackTrace()
等接口用於獲取堆棧跟蹤數據等信息。程序員
主要方法:github
fillInStackTrace
- 用當前的調用棧層次填充 Throwable
對象棧層次,添加到棧層次任何先前信息中。getMessage
- 返回關於發生的異常的詳細信息。這個消息在 Throwable
類的構造函數中初始化了。getCause
- 返回一個 Throwable
對象表明異常緣由。getStackTrace
- 返回一個包含堆棧層次的數組。下標爲 0 的元素表明棧頂,最後一個元素表明方法調用堆棧的棧底。printStackTrace
- 打印 toString()
結果和棧層次到 System.err
,即錯誤輸出流。toString
- 使用 getMessage
的結果返回表明 Throwable
對象的字符串。Error
是 Throwable
的一個子類。Error
表示合理的應用程序不該該嘗試捕獲的嚴重問題。大多數此類錯誤都是異常狀況。編譯器不會檢查 Error
。數據庫
常見 Error
:編程
AssertionError
- 斷言錯誤。VirtualMachineError
- 虛擬機錯誤。UnsupportedClassVersionError
- Java 類版本錯誤。StackOverflowError
- 棧溢出錯誤。OutOfMemoryError
- 內存溢出錯誤。Exception
是 Throwable
的一個子類。Exception
表示合理的應用程序可能想要捕獲的條件。數組
**編譯器會檢查 Exception
異常。**此類異常,要麼經過 throws
進行聲明拋出,要麼經過 try catch
進行捕獲處理,不然不能經過編譯。安全
常見 Exception
:bash
ClassNotFoundException
- 應用程序試圖加載類時,找不到相應的類,拋出該異常。CloneNotSupportedException
- 當調用 Object 類中的 clone 方法克隆對象,但該對象的類沒法實現 Cloneable 接口時,拋出該異常。IllegalAccessException
- 拒絕訪問一個類的時候,拋出該異常。InstantiationException
- 當試圖使用 Class 類中的 newInstance 方法建立一個類的實例,而指定的類對象由於是一個接口或是一個抽象類而沒法實例化時,拋出該異常。InterruptedException
- 一個線程被另外一個線程中斷,拋出該異常。NoSuchFieldException
- 請求的變量不存在。NoSuchMethodException
- 請求的方法不存在。示例:
public class ExceptionDemo {
public static void main(String[] args) {
Method method = String.class.getMethod("toString", int.class);
}
};
複製代碼
試圖編譯運行時會報錯:
Error:(7, 47) java: 未報告的異常錯誤java.lang.NoSuchMethodException; 必須對其進行捕獲或聲明以便拋出
複製代碼
RuntimeException
是 Exception
的一個子類。RuntimeException
是那些可能在 Java 虛擬機正常運行期間拋出的異常的超類。
**編譯器不會檢查 RuntimeException
異常。**當程序中可能出現這類異常時,假若既沒有經過 throws
聲明拋出它,也沒有用 try catch
語句捕獲它,程序仍是會編譯經過。
示例:
public class RuntimeExceptionDemo {
public static void main(String[] args) {
// 此處產生了異常
int result = 10 / 0;
System.out.println("兩個數字相除的結果:" + result);
System.out.println("----------------------------");
}
};
複製代碼
運行時輸出:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at io.github.dunwu.javacore.exception.RumtimeExceptionDemo01.main(RumtimeExceptionDemo01.java:6)
複製代碼
常見 RuntimeException
:
ArrayIndexOutOfBoundsException
- 用非法索引訪問數組時拋出的異常。若是索引爲負或大於等於數組大小,則該索引爲非法索引。ArrayStoreException
- 試圖將錯誤類型的對象存儲到一個對象數組時拋出的異常。ClassCastException
- 當試圖將對象強制轉換爲不是實例的子類時,拋出該異常。IllegalArgumentException
- 拋出的異常代表向方法傳遞了一個不合法或不正確的參數。IllegalMonitorStateException
- 拋出的異常代表某一線程已經試圖等待對象的監視器,或者試圖通知其餘正在等待對象的監視器而自己沒有指定監視器的線程。IllegalStateException
- 在非法或不適當的時間調用方法時產生的信號。換句話說,即 Java 環境或 Java 應用程序沒有處於請求操做所要求的適當狀態下。IllegalThreadStateException
- 線程沒有處於請求操做所要求的適當狀態時拋出的異常。IndexOutOfBoundsException
- 指示某排序索引(例如對數組、字符串或向量的排序)超出範圍時拋出。NegativeArraySizeException
- 若是應用程序試圖建立大小爲負的數組,則拋出該異常。NullPointerException
- 當應用程序試圖在須要對象的地方使用 null 時,拋出該異常NumberFormatException
- 當應用程序試圖將字符串轉換成一種數值類型,但該字符串不能轉換爲適當格式時,拋出該異常。SecurityException
- 由安全管理器拋出的異常,指示存在安全侵犯。StringIndexOutOfBoundsException
- 此異常由 String 方法拋出,指示索引或者爲負,或者超出字符串的大小。UnsupportedOperationException
- 當不支持請求的操做時,拋出該異常。自定義一個異常類,只須要繼承 Exception
或 RuntimeException
便可。
示例:
public class MyExceptionDemo {
public static void main(String[] args) {
throw new MyException("自定義異常");
}
static class MyException extends RuntimeException {
public MyException(String message) {
super(message);
}
}
}
複製代碼
輸出:
Exception in thread "main" io.github.dunwu.javacore.exception.MyExceptionDemo$MyException: 自定義異常
at io.github.dunwu.javacore.exception.MyExceptionDemo.main(MyExceptionDemo.java:9)
複製代碼
若是想在程序中明確地拋出異常,須要用到 throw
和 throws
。
若是一個方法沒有捕獲一個檢查性異常,那麼該方法必須使用 throws
關鍵字來聲明。throws
關鍵字放在方法簽名的尾部。
throw
示例:
public class ThrowDemo {
public static void f() {
try {
throw new RuntimeException("拋出一個異常");
} catch (Exception e) {
System.out.println(e);
}
}
public static void main(String[] args) {
f();
}
};
複製代碼
輸出:
java.lang.RuntimeException: 拋出一個異常
複製代碼
也可使用 throw
關鍵字拋出一個異常,不管它是新實例化的仍是剛捕獲到的。
throws
示例:
public class ThrowsDemo {
public static void f1() throws NoSuchMethodException, NoSuchFieldException {
Field field = Integer.class.getDeclaredField("digits");
if (field != null) {
System.out.println("反射獲取 digits 方法成功");
}
Method method = String.class.getMethod("toString", int.class);
if (method != null) {
System.out.println("反射獲取 toString 方法成功");
}
}
public static void f2() {
try {
// 調用 f1 處,若是不用 try catch ,編譯時會報錯
f1();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
f2();
}
};
複製代碼
輸出:
反射獲取 digits 方法成功
java.lang.NoSuchMethodException: java.lang.String.toString(int)
at java.lang.Class.getMethod(Class.java:1786)
at io.github.dunwu.javacore.exception.ThrowsDemo.f1(ThrowsDemo.java:12)
at io.github.dunwu.javacore.exception.ThrowsDemo.f2(ThrowsDemo.java:21)
at io.github.dunwu.javacore.exception.ThrowsDemo.main(ThrowsDemo.java:30)
複製代碼
throw 和 throws 的區別:
使用 try 和 catch 關鍵字能夠捕獲異常。try catch 代碼塊放在異常可能發生的地方。
它的語法形式以下:
try {
// 可能會發生異常的代碼塊
} catch (Exception e1) {
// 捕獲並處理try拋出的異常類型Exception
} catch (Exception2 e2) {
// 捕獲並處理try拋出的異常類型Exception2
} finally {
// 不管是否發生異常,都將執行的代碼塊
}
複製代碼
此外,JDK7 之後,catch
多種異常時,也能夠像下面這樣簡化代碼:
try {
// 可能會發生異常的代碼塊
} catch (Exception | Exception2 e) {
// 捕獲並處理try拋出的異常類型
} finally {
// 不管是否發生異常,都將執行的代碼塊
}
複製代碼
try
- try
語句用於監聽。將要被監聽的代碼(可能拋出異常的代碼)放在 try
語句塊以內,當 try
語句塊內發生異常時,異常就被拋出。catch
- catch
語句包含要捕獲異常類型的聲明。當保護代碼塊中發生一個異常時,try
後面的 catch
塊就會被檢查。finally
- finally
語句塊老是會被執行,不管是否出現異常。try catch
語句後不必定非要finally
語句。finally
經常使用於這樣的場景:因爲finally
語句塊老是會被執行,因此那些在 try
代碼塊中打開的,而且必須回收的物理資源(如數據庫鏈接、網絡鏈接和文件),通常會放在finally
語句塊中釋放資源。try
、catch
、finally
三個代碼塊中的局部變量不可共享使用。catch
塊嘗試捕獲異常時,是按照 catch
塊的聲明順序從上往下尋找的,一旦匹配,就不會再向下執行。所以,若是同一個 try
塊下的多個 catch
異常類型有父子關係,應該將子類異常放在前面,父類異常放在後面。示例:
public class TryCatchFinallyDemo {
public static void main(String[] args) {
try {
// 此處產生了異常
int temp = 10 / 0;
System.out.println("兩個數字相除的結果:" + temp);
System.out.println("----------------------------");
} catch (ArithmeticException e) {
System.out.println("出現異常了:" + e);
} finally {
System.out.println("不論是否出現異常,都執行此代碼");
}
}
};
複製代碼
運行時輸出:
出現異常了:java.lang.ArithmeticException: / by zero
不論是否出現異常,都執行此代碼
複製代碼
異常鏈是以一個異常對象爲參數構造新的異常對象,新的異常對象將包含先前異常的信息。
經過使用異常鏈,咱們能夠提升代碼的可理解性、系統的可維護性和友好性。
咱們有兩種方式處理異常,一是 throws 拋出交給上級處理,二是 try…catch 作具體處理。try…catch 的 catch 塊咱們能夠不須要作任何處理,僅僅只用 throw 這個關鍵字將咱們封裝異常信息主動拋出來。而後在經過關鍵字 throws 繼續拋出該方法異常。它的上層也能夠作這樣的處理,以此類推就會產生一條由異常構成的異常鏈。
示例:
public class ExceptionChainDemo {
static class MyException1 extends Exception {
public MyException1(String message) {
super(message);
}
}
static class MyException2 extends Exception {
public MyException2(String message, Throwable cause) {
super(message, cause);
}
}
public static void f1() throws MyException1 {
throw new MyException1("出現 MyException1");
}
public static void f2() throws MyException2 {
try {
f1();
} catch (MyException1 e) {
throw new MyException2("出現 MyException2", e);
}
}
public static void main(String[] args) throws MyException2 {
f2();
}
}
複製代碼
輸出:
Exception in thread "main" io.github.dunwu.javacore.exception.ExceptionChainDemo$MyException2: 出現 MyException2
at io.github.dunwu.javacore.exception.ExceptionChainDemo.f2(ExceptionChainDemo.java:29)
at io.github.dunwu.javacore.exception.ExceptionChainDemo.main(ExceptionChainDemo.java:34)
Caused by: io.github.dunwu.javacore.exception.ExceptionChainDemo$MyException1: 出現 MyException1
at io.github.dunwu.javacore.exception.ExceptionChainDemo.f1(ExceptionChainDemo.java:22)
at io.github.dunwu.javacore.exception.ExceptionChainDemo.f2(ExceptionChainDemo.java:27)
... 1 more
複製代碼
這篇文章中對於異常鏈講解比較詳細。
Java 異常處理中 finally
中的 return
會覆蓋 catch
代碼塊中的 return
語句和 throw
語句,因此 Java 不建議在 finally
中使用 return
語句。
此外 finally
中的 throw
語句也會覆蓋 catch
代碼塊中的 return
語句和 throw
語句。
示例:
public class FinallyOverrideExceptionDemo {
static void f() throws Exception {
try {
throw new Exception("A");
} catch (Exception e) {
throw new Exception("B");
} finally {
throw new Exception("C");
}
}
public static void main(String[] args) {
try {
f();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
複製代碼
輸出:C
當子類重寫父類帶有 throws
聲明的函數時,其 throws
聲明的異常必須在父類異常的可控範圍內——用於處理父類的 throws
方法的異常處理器,必須也適用於子類的這個帶 throws
方法 。這是爲了支持多態。
示例:
public class ExceptionOverrideDemo {
static class Father {
public void start() throws IOException {
throw new IOException();
}
}
static class Son extends Father {
@Override
public void start() throws SQLException {
throw new SQLException();
}
}
public static void main(String[] args) {
Father obj1 = new Father();
Father obj2 = new Son();
try {
obj1.start();
obj2.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
複製代碼
上面的示例編譯時會報錯,緣由在於:
由於 Son 類拋出異常的實質是
SQLException
,而IOException
沒法處理它。那麼這裏的 try catch 就不能處理 Son 中的異常了。多態就不能實現了。
若是 Java 程序只有一個線程,那麼沒有被任何代碼處理的異常會致使程序終止。若是 Java 程序是多線程的,那麼沒有被任何代碼處理的異常僅僅會致使異常所在的線程結束。
ArithmeticException
,就應該 catch ArithmeticException
,而不是 catch 範圍較大的 RuntimeException
,甚至是 Exception
。finally
塊拋出異常或者返回值擴展閱讀: