異常指不期而至的各類情況,如:文件找不到、網絡鏈接失敗、非法參數等。異常是一個事件,它發生在程序運行期間,干擾了正常的指令流程。Java通 過API中Throwable類的衆多子類描述各類不一樣的異常。於是,Java異常都是對象,是Throwable子類的實例,描述了出如今一段編碼中的錯誤條件。當條件生成時,錯誤將引起異常。java
Java異常類層次結構圖:程序員
在 Java 中,全部的異常都有一個共同的祖先 Throwable(可拋出)。Throwable 指定代碼中可用異常傳播機制經過 Java 應用程序傳輸的任何問題的共性。數據庫
一、Throwable: 有兩個重要的子類:Exception和 Error兩者都是 Java 異常處理的重要子類,各自都包含大量子類。編程
注意:異常和錯誤的區別:異常能被程序自己能夠處理,錯誤是沒法處理。數組
二、一般,Java的異常分爲可查的異常(checked exceptions)和不可查的異常(unchecked exceptions)。安全
三、Exception 又可分兩大類:運行時異常和非運行時異常(編譯異常),程序中應當儘量去處理這些異常。網絡
運行時異常的特色是Java編譯器不會檢查它,也就是說,當程序中可能出現這類異常,即便沒有用try-catch語句捕獲它,也沒有用throws子句聲明拋出它,也會編譯經過。函數
在 Java 應用程序中,異常處理機制爲:拋出異常,捕捉異常。ui
對於不可查異常(RuntimeException、Error)或可查異常(除了RuntimeException及其子類之外,其餘的Exception類及其子類),Java技術所要求的異常處理方式有所不一樣。編碼
可以捕捉異常的方法,須要提供相符類型的異常處理器。所捕捉的異常,多是因爲自身語句所引起並拋出的異常,也多是由某個調用的方法或者Java運行時 系統等拋出的異常。也就是說,一個方法所能捕捉的異常,必定是Java代碼在某處所拋出的異常。簡單地說,異常老是先被拋出,後被捕捉的。
任何Java代碼均可以拋出異常,如:本身編寫的代碼、來自Java開發環境包中代碼,或者Java運行時系統。不管是誰,均可以經過Java的throw語句拋出異常。
從方法中拋出的任何異常都必須使用throws子句。
捕捉異常經過try-catch語句或者try-catch-finally語句實現。
整體來講,Java規定:對於可查異常必須捕捉、或者聲明拋出。容許忽略不可查的RuntimeException和Error。
在Java中,異常經過try-catch語句捕獲,其通常語法形式爲:
try { // 可能會發生異常的程序代碼 } catch (Type1 id1){ // 捕獲並處置try拋出的異常類型Type1 } catch (Type2 id2){ //捕獲並處置try拋出的異常類型Type2 }
關鍵字try後的一對大括號將一塊可能發生異常的代碼包起來,稱爲監控區域(guarded region)。Java方法在運行過程當中出現異常,則建立異常對象。將異常拋出監控區域之 外,Java異常處理機制將負責搜尋參數與異常類型相匹配的第一個處理程序。如有匹配的catch子句,則運行其異常處理代碼,try-catch語句結束。
匹配的原則是:若是拋出的異常對象屬於catch子句的異常類,或者屬於該異常類的子類,則認爲生成的異常對象與catch塊捕獲的異常類型相匹配。
public class TestException1 { public static void main(String[] args) { int a = 6; int b = 0; try { // try監控區域 if (b == 0) throw new ArithmeticException(); // 經過throw語句拋出異常 System.out.println("a/b的值是:" + a / b); } catch (ArithmeticException e) { // catch捕捉異常 System.out.println("程序出現異常,變量b不能爲0。"); } System.out.println("程序正常結束。"); } }
輸出:
程序出現異常,變量b不能爲0。 程序正常結束。
上例中在try監控區域經過if語句進行判斷,當「除數爲0」的錯誤條件成立時引起ArithmeticException異常,建立 ArithmeticException異常對象,並由throw語句將異常拋給Java運行時系統,由系統尋找匹配的異常處理器catch並運行相應異常處理代碼。
事實上,「除數爲0」等ArithmeticException異常,是RuntimException的子類。而運行時異常將由運行時系統自動拋出,不須要使用throw語句。
public class TestException1 { public static void main(String[] args) { int a = 6; int b = 0; try { // try監控區域 System.out.println("a/b的值是:" + a / b); } catch (ArithmeticException e) { // catch捕捉異常 System.out.println("程序出現異常,變量b不能爲0。"); } System.out.println("程序正常結束。"); } }
輸出結果:
程序出現異常,變量b不能爲0。 程序正常結束。
上例中System.out.println("a/b的值是:" + a/b)語句,在運行中出現「除數爲0」錯誤,引起ArithmeticException異常。運行時系統建立異常對象並拋出監控區域,轉而匹配合適的異常處理器catch,並執行相應的異常處理代碼。
因爲檢查運行時異常的代價遠大於捕捉異常所帶來的益處,運行時異常不可查。Java編譯器容許忽略運行時異常,一個方法能夠既不捕捉,也不聲明拋出運行時異常。
public class TestException { public static void main(String[] args) { int a, b; a = 6; b = 0; // 除數b 的值爲0 System.out.println(a / b); } }
輸出結果:
Exception in thread "main" java.lang.ArithmeticException: / by zero at Test.TestException.main(TestException.java:8)
public class TestException2 { public static void main(String[] args) { int[] intArray = new int[3] ; try { for(int i=0;i <= intArray.length; i++) { intArray[i] = i; System.out.println("intArray[" + i + "] = " + intArray[i]); System.out.println("intArray[" + i + "] / " + (i-2) + " = " + intArray[i] / (i-2)); } }catch(ArrayIndexOutOfBoundsException e) { System.out.println("intArray數組下表越界異常。"); }catch(ArithmeticException e) { System.out.println("除數爲0異常。"); } System.out.println("程序正常結束"); } }
輸出結果:
intArray[0] = 0 intArray[0] / -2 = 0 intArray[1] = 1 intArray[1] / -1 = -1 intArray[2] = 2 除數爲0異常。 程序正常結束
程序可能會出現除數爲0異常,還可能會出現數組下標越界異常。程序運行過程當中ArithmeticException異常類型是先行匹配的,所以執行相匹配的catch語句:
catch(ArrayIndexOutOfBoundsException e) { System.out.println("intArray數組下表越界異常。"); }
須要注意的是,一旦某個catch捕獲到匹配的異常類型,將進入異常處理代碼,一旦處理結束就意味着整個try-catch語句結束,其餘的catch子句再也不有匹配和捕獲異常類型的機會。
try-catch語句還能夠包括第三部分,就是finally子句。它表示不管是否出現異常,都應當執行的內容。try-catch-finally語句的通常語法形式爲:
try { // 可能會發生異常的程序代碼 } catch (Type1 id1) { // 捕獲並處理try拋出的異常類型Type1 } catch (Type2 id2) { // 捕獲並處理try拋出的異常類型Type2 } finally { // 不管是否發生異常,都將執行的語句塊 }
public class TestException3 { public static void main(String args[]) { int i = 0; String[] greetings = { " Hello world !", " Hello World !! ", " HELLO WORLD !!!" }; while (i < 4) { try { // 特別注意循環控制變量i的設計,避免形成無限循環 System.out.println(greetings[i++]); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("數組下標越界異常"); } finally { System.out.println("--------------------------"); } } } }
輸出以下:
Hello world ! -------------------------- Hello World !! -------------------------- HELLO WORLD !!! -------------------------- 數組下標越界異常 --------------------------
小結:
一、try 塊:用於捕獲異常。其後可接零個或多個catch塊,若是沒有catch塊,則必須跟一個finally塊;
二、catch 塊:用於處理try捕獲到的異常;
三、finally 塊:不管是否捕獲或處理異常,finally塊裏的語句都會被執行。當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回以前被執行。在如下4種特殊狀況下,finally塊不會被執行:
任何Java代碼均可以拋出異常,如:本身編寫的代碼、來自Java開發環境包中代碼,或者Java運行時系統。不管是誰,均可以經過Java的throw語句拋出異常。從方法中拋出的任何異常都必須使用throws子句。
若是一個方法可能會出現異常,但沒有能力處理這種異常,能夠在方法聲明處用throws子句來聲明拋出異常。例如汽車在運行時可能會出現故障,汽車自己沒辦法處理這個故障,那就讓開車的人來處理。
throws語句用在方法定義時聲明該方法要拋出的異常類型,若是拋出的是Exception異常類型,則該方法被聲明爲拋出全部的異常。多個異常可以使用逗號分割。throws語句的語法格式爲:
methodname throws Exception1,Exception2,..,ExceptionN { }
方法名後的throws Exception1,Exception2,...,ExceptionN 爲聲明要拋出的異常列表。當方法拋出異常列表的異常時,方法將不對這些類型及其子類類型的異常做處理,而拋向調用該方法的方法,由他去處理。例如:
public class TestException4 { static void pop() throws NegativeArraySizeException { // 定義方法並拋出NegativeArraySizeException異常 int[] arr = new int[-3]; // 建立數組 } public static void main(String[] args) { try { // try語句處理異常信息 pop(); // 調用pop()方法 } catch (NegativeArraySizeException e) { System.out.println("pop()方法拋出的異常!");// 輸出異常信息 } } }
輸出:
pop()方法拋出的異常
使用throws關鍵字將異常拋給調用者後,若是調用者不想處理該異常,能夠繼續向上拋出,但最終要有可以處理該異常的調用者。
pop方法沒有處理異常NegativeArraySizeException,而是由main函數來處理。
Throws拋出異常的規則:
例如:
void method1() throws IOException{} //合法 //編譯錯誤,必須捕獲或聲明拋出IOException void method2(){ method1(); } //合法,聲明拋出IOException void method3()throws IOException { method1(); } //合法,聲明拋出Exception,IOException是Exception的子類 void method4()throws Exception { method1(); } //合法,捕獲IOException void method5(){ try{ method1(); }catch(IOException e){…} } //編譯錯誤,必須捕獲或聲明拋出Exception void method6(){ try{ method1(); }catch(IOException e){throw new Exception();} } //合法,聲明拋出Exception void method7()throws Exception{ try{ method1(); }catch(IOException e){throw new Exception();} }
throw老是出如今函數體中,用來拋出一個Throwable類型的異常。程序會在throw語句後當即終止,它後面的語句執行不到,而後在包含它的全部try塊中(可能在上層調用函數中)從裏向外尋找含有與其匹配的catch子句的try塊。
咱們知道,異常是異常類的實例對象,咱們能夠建立異常類的實例對象經過throw語句拋出。該語句的語法格式爲:
throw new exceptionname(xx);
例如拋出一個IOException類的異常對象:
throw new IOException(xx);
要注意的是,throw 拋出的只可以是可拋出類Throwable 或者其子類的實例對象。下面的操做是錯誤的:
throw new String("exception");
這是由於String 不是Throwable 類的子類。
若是拋出了檢查異常,則還應該在方法頭部聲明方法可能拋出的異常類型,該方法的調用者應該處理拋出的異常;
public class TestException5 {
static int quotient(int x, int y) throws MyException { // 定義方法拋出異常
if (y < 0) { // 判斷參數是否小於0
throw new MyException("除數不能是負數!"); // 異常信息
}
return x/y; // 返回值
}
public static void main(String args[]) { // 主方法
int a =3;
int b =-1;
try { // try語句包含可能發生異常的語句
int result = quotient(a, b); // 調用方法quotient()
} catch (MyException e) { // 處理自定義異常
System.out.println(e.getMessage()); // 輸出異常信息
} catch (ArithmeticException e) { // 處理ArithmeticException異常
System.out.println("除數不能爲0!"); // 輸出提示信息
} catch (Exception e) { // 處理其餘異常
System.out.println("程序發生了其餘的異常!"); // 輸出提示信息
}
}
}
class MyException extends Exception { // 建立自定義異常類
String message; // 定義String類型變量
public MyException(String ErrorMessagr) { // 父類方法
message = ErrorMessagr;
}
public String getMessage() { // 覆蓋getMessage()方法
return message;
}
}
若是調用者不處理該異常,全部方法都層層上拋獲取的異常,最終JVM會進行處理,處理也很簡單,就是打印異常消息和堆棧信息;
public class TestException5 { static int quotient(int x, int y) throws MyException { // 定義方法拋出異常 if (y < 0) { // 判斷參數是否小於0 throw new MyException("除數不能是負數!"); // 異常信息 } return x/y; // 返回值 } public static void main(String args[]) throws MyException { // 主方法 int a =3; int b =-1; quotient(a,b); } } class MyException extends Exception { // 建立自定義異常類 String message; // 定義String類型變量 public MyException(String ErrorMessagr) { // 父類方法 message = ErrorMessagr; } public String getMessage() { // 覆蓋getMessage()方法 return message; } }
輸出以下:
Exception in thread "main" MyException: 除數不能是負數! at TestException5.quotient(TestException5.java:4) at TestException5.main(TestException5.java:12)
Java方法拋出的可查異常將依據調用棧、沿着方法調用的層次結構一直傳遞到具有處理能力的調用方法,最高層次到main方法爲止。若是異常傳遞到main方法,而main不具有處理能力,也沒有經過throws聲明拋出該異常,將可能出現編譯錯誤。
若是調用quotient(5,0),將會因「除數爲0」錯誤引起ArithmeticException異常,屬於運行時異常類,由Java運行時系統自動拋出,quotient()方法沒有捕捉ArithmeticException異常,Java運行時系統將沿方法調用棧查到main方法,將拋出的異常上傳至quotient()方法的調用者。
注意:catch關鍵字後面括號中的Exception類型的參數e,Exception就是try代碼塊傳遞給catch代碼塊的變量類型,e就是變量名,catch代碼塊中語句"e.getMessage();"用於輸出錯誤性質。一般異常處理經常使用3個函數來獲取異常的有關信息:
有時爲了簡單會忽略掉catch語句後的代碼,這樣try-catch語句就成了一種擺設,一旦程序在運行過程當中出現了異常,就會忽略處理異常,而錯誤發生的緣由很難查找。
在Java中提供了一些異經常使用來描述常常發生的錯誤,對於這些異常,有的須要程序員進行捕獲處理或聲明拋出,有的是由Java虛擬機自動進行捕獲處理。Java中常見的異常類:
使用Java內置的異常類能夠描述在編程時出現的大部分異常狀況。除此以外,用戶還能夠自定義異常,用戶自定義異常類,只需繼承Exception類便可。
在程序中使用自定義異常類,大致可分爲如下幾個步驟。
(1)建立自定義異常類。
(2)在方法中經過throw關鍵字拋出異常對象。
(3)若是在當前拋出異常的方法中處理異常,可使用try-catch語句捕獲並處理;不然在方法的聲明處經過throws關鍵字指明要拋出給方法調用者的異常,繼續進行下一步操做。
(4)在出現異常方法的調用者中捕獲並處理異常。
參考文獻:
[1]深刻理解java異常處理機制(轉載)