Java異常機制
Java異常機制
異常
異常是程式中的一些錯誤。但並不是所有的錯誤都是異常,並且錯誤有時候是可以避免的。
異常的分類:
- 檢查性異常:最具代表的檢查性異常是用戶錯誤或問題引起的異常,這是程式設計師無法預見的。例如要打開一個不存在文件時,一個異常就發生了,這些異常在編譯時不能被簡單地忽略。
- 運行時異常: 運行時異常是可能被程式設計師避免的異常。與檢查性異常相反,運行時異常可以在編譯時被忽略。
- 錯誤(Error): 錯誤不是異常,而是脫離程式設計師控制的問題。錯誤在程式碼中通常被忽略。例如,當棧溢出時,一個錯誤就發生了,在編譯時同樣檢查不到。
示例:
package com.wmwx.exception;
public class Demo01 {
public static void main(String[] args) {
new Demo01().a(); //拋出異常:StackOverflowError
System.out.println(11/0); //拋出異常:ArithmeticException
System.out.println() //拋出異常:Error
}
public void a(){
b();
}
public void b(){
a();
}
}
異常體系結構
Java把異常當做對象來處理,並定義了一個基類java.lang.Throwable作為所有異常的超類。這些異常分為兩大類,即異常(Exception)和錯誤(Error)。其中,Exception類有兩個主要的子類,即 IOException 類和 RuntimeException 類。
Error
Error類對象由虛擬機生成並拋出,大多數錯誤與程式碼編寫者所執行的操作無關。
常見的錯誤如Java虛擬機運行錯誤(VirtualMachineError),當JVM不再有繼續執行操作所需的記憶體資源時,將出現OutOfMemoryError。這些異常發生時,Java虛擬機一般會選擇將執行緒中止。
還有發生在虛擬機試圖執行應用時的錯誤,如類定義錯誤(NoClassDefFoundError)、鏈接錯誤(LinkageError)等,這些錯誤是不可查的。因為它們在應用程式的控制和處理程式能力之外,而且絕大多數是程式運行時不允許出現的狀況。
Exception
在Exception類的分支中有一個重要的子類 RuntimeException(運行時異常),其中包含以下異常:
-
ArrayIndexOutOfBoundsException(數組下標越界)
-
NullPointerException(空指針異常)
-
ArithmeticException(算術異常)
-
MissingResourceException(丟失資源)
-
ClassNotFoundException(找不到類)
這些異常是不檢查異常,程式中可以選擇捕獲處理,也可以不處理。這些異常一般是由程式邏輯錯誤引起的,程式應該從邏輯角度儘可能避免這類異常的發生。
Error和Exception的區別:
- Error 通常是災難性的致命的錯誤,是程式無法控制和處理的,當出現這些異常時,Java虛擬機(JVM)一般會選擇終止執行緒
- Exception 通常情況下是可以被程式處理的,並且在程式中應該儘可能的去處理這些異常。
捕獲異常
使用 try 和 catch 關鍵字可以捕獲異常。try/catch 程式碼塊放在異常可能發生的地方。
try/catch程式碼塊中的程式碼稱為保護程式碼,其基本語法如下:
try
{
// 程式程式碼
}catch(ExceptionName e1)
{
// Catch 塊
}catch(ExceptionName e2) //e2需比e1範圍更大
{
// Catch 塊
}finally {
// 終究會執行
}
示例:
package com.wmwx.exception;
public class Demo02 {
public static void main(String[] args) {
int a = 11;
int b = 0;
//快捷鍵 ctrl+alt+T
try{ //try 監控區域
System.out.println(a/b);
}catch (ArithmeticException e){ //catch 捕獲異常(參數是異常類型)
System.out.println("程式出現異常,變數b不能為0!");
}finally { //finally 善後工作
System.out.println("finally終究會被執行。");
}
try{ //監控區域
new Demo02().a();
}catch (StackOverflowError e){ //捕獲異常,多個類別需要從小到大
System.out.println("StackOverflowError"); //會輸出
}catch (Exception e){
System.out.println("Exception"); //不會輸出
}catch (Throwable t){
System.out.println("Throwable"); //不會輸出
}finally { //善後工作
System.out.println("finally終究會被執行。");
}
}
public void a(){
b();
}
public void b(){
a();
}
}
拋出異常
如果一個方法沒有捕獲到一個檢查性異常,那麼該方法必須使用 throws 關鍵字來聲明。throws 關鍵字放在方法簽名的尾部。
也可以使用 throw 關鍵字拋出一個異常,無論它是新實例化的還是剛捕獲到的。
注意:兩種方式的拼寫不同,第一種末尾有 s,第二種沒有。
示例:
package com.wmwx.exception;
public class Demo03 {
public static void main(String[] args) {
int a = 11;
int b = 0;
try {
new Demo03().devide(a, b);
} catch (ArithmeticException e) {
e.printStackTrace();
} finally {
}
}
//假設在方法中無法處理異常,可以用throws主動拋出異常
public void devide(int a, int b) throws ArithmeticException{
if (b==0){
throw new ArithmeticException(); //使用throw主動拋出異常,一般用在方法中
}
System.out.println(a / b);
}
}
自定義異常
使用Java內置的異常類可以描述在編程時出現的大部分異常精況。除此之外,用戶還可以自定義異常,只需繼承Exception類即可。
示例:
MyException類(自定義異常類):
package com.wmwx.exception;
//自定義的異常類
public class MyException extends Exception{
//假設:當傳遞過來的數字大於10時拋出異常
private int detail;
//構造方法
public MyException(int a) {
this.detail = a;
}
//toString 列印異常資訊
@Override
public String toString() {
return "MyException{detail=" + detail + "}";
}
}
Demo04類(測試類):
package com.wmwx.exception;
//自定義異常的測試類
public class Demo04 {
//可能會存在異常的方法
public void test(int a) throws MyException {
System.out.println("傳遞的參數為:"+a);
if (a>10){
throw new MyException(a); //拋出異常
}
System.out.println("沒有異常!");
}
public static void main(String[] args) {
try {
new Demo04().test(5); //沒有異常
new Demo04().test(15); //有異常
} catch (MyException e) {
System.out.println("MyException => " + e);
}
}
}
經驗總結
- 處理運行時異常時,採用邏輯去合理規避,同時輔助 try-catch 處理。
- 在多重catch塊後面,可以加一個 catch (Exception) 來處理可能會被遺漏的異常。對於不確定的程式碼,也可以加上try-catch,處理潛在的異常。
- 盡量去處理異常,切忌只是簡單地調用 printStackTrace() 去列印輸出。具體如何處理異常,要根據不同的業務需求和異常類型去決定。
- 盡量添加 finally 語句塊去釋放佔用的資源。