異常
一說談到異常,可能就有小夥伴說,這個啊,我 try...catch...finally用得賊溜。別急哈,後面有個 案例,看完之後你肯定會覺得自己對異常的理解也不是那麼透徹了,同時本文還可能會掃到你的一些知識盲點。額,扯遠了~
異常一般指不期而至的各種狀況,如:檔案不存在、空指標、非法引數等。
異常是一個事件,發生在程式執行期間,干擾了正常的指令流程。
Java 中使用 Throwable 類及其子類來描述各種不同的異常。因此,Java 異常都是物件,是 Throwable 的子類例項,描述了出現在一段編碼中的錯誤條件。當條件生成時,錯誤將引發異常。
Java 異常類層次結構圖:
從圖上我們可以看到,Java 異常都繼承自 Throwable,Throwable 分為兩大派系,一類是 Error(錯誤),一類是 Exception(異常)。
Error
Error 是程式設計師無法處理的錯誤
,表示執行應用程式中教嚴重的問題。大多數錯誤與程式碼編寫者執行的操作無關,而表示程式碼執行時 JVM 出現的問題。比如,Java 虛擬機器執行時錯誤(VirtualMachineError),當 JVM 不再有繼續執行操作所需的記憶體資源時,將出現 OutOfMemoryError。這時異常發生時,Java 虛擬機器一般會終止執行緒。
所以這一類異常我們一般不用太糾結。
Exception
這是程式本身可以處理的異常。
Exception 有一個非常重要的子類 RuntimeException,RuntimeException 類及其子類表示 JVM 常用操作引發的錯誤,例如空值物件引用、除數為零、陣列角標越界則分別會引發 NullPointException、ArithmeticException、ArrayIndexOutOfBoundException。
敲黑板!!分清異常和錯誤的區別,異常是可以被程式本身處理的,錯誤不能。
可查異常
又稱編譯時異常,是編譯器要求必須處置的異常。
正確的程式在執行中,很容易出現的、情理可容的異常狀況。可查異常雖然是異常狀況,但在一定程度上它的發生是可以預計的,而且一旦發生這種異常狀況,就必須採取某種方式進行處理。
除了RuntimeException及其子類以外,其他的Exception類及其子類都屬於可查異常。這種異常的特點是Java編譯器會檢查它,也就是說,當程式中可能出現這類異常,要麼用try-catch語句捕獲它,要麼用throws子句宣告丟擲它,否則編譯不會通過。
不可查異常
編譯器不要求強制處置的異常,包括執行時異常(RuntimeException)和錯誤(Error)。
執行時異常
都是 RuntimeException 類及其子類。
這些異常是不檢查異常
,程式中可以選擇捕獲處理,也可以不處理。這些異常一般是由程式邏輯錯誤引起的,程式應該從邏輯角度儘可能避免這類異常的發生。
編譯時異常
是RuntimeException以外的異常,型別上都屬於Exception類及其子類。從程式語法角度講是必須進行處理的異常,如果不處理,程式就不能編譯通過。
處理異常機制
遇到異常怎麼辦?要麼自己想辦法處理,要麼就拋給別人。
丟擲異常:
當一個方法出現錯誤引發異常時,方法建立異常物件並交付給執行時系統,系統物件中包含了異常型別和異常出現時的程式狀態等異常資訊。執行時系統負責尋找處置異常的程式碼並執行。
捕獲異常:
在方法丟擲異常之後,執行時系統將轉為尋找合適的異常處理器(Exception handler)。潛在的異常處理是異常發生時依次存留在呼叫棧中的方法的集合。當異常處理器所能處理的異常型別與丟擲的異常型別相符時,即為合適的異常處理器。執行時系統從發生異常的方法開始,依次回查呼叫棧中的方法,直至找到含有異常處理器的方法並執行。當執行時系統遍歷了呼叫棧都沒找到合適的異常處理器,則執行時系統終止,Java 程式終止。
對於執行時異常、錯誤和可查異常,Java 要求的異常處理方式有所不同。
- 對於方法執行中可能出現的 Error,當執行方法不能捕捉時,Java 允許該方法不做任何丟擲宣告。因為大多數 Error 異常屬於永遠不允許發生的情況,也屬於合理的應用程式不掛捕獲的異常。
- 對於所有可查異常,Java 規定:一個方法必須捕捉或者宣告丟擲。
能夠捕捉異常的方法,需要提供相符型別的異常處理器。也就是說,一個方法所能捕獲的異常,一點是 Java 程式碼在某處所丟擲的異常
。
任何Java程式碼都可以丟擲異常,如:自己編寫的程式碼、來自Java開發環境包中程式碼,或者Java執行時系統。無論是誰,都可以通過Java的throw語句丟擲異常。
從方法中丟擲的任何異常都必須使用throws子句。
捕捉異常通過try-catch語句或者try-catch-finally語句實現。
總結來書。Java 規定:對於可查異常必須捕捉、或者宣告丟擲。執行忽略不可查的 RuntimeException 和 Error。
捕獲異常 try、catch 和 finally
- 首先是 try-catch 語句
基本語法如下:
try {
// 可能會發生異常的程式程式碼
} catch (Type1 id1){
// 捕獲並處置try丟擲的異常型別Type1
} catch (Type2 id2){
//捕獲並處置try丟擲的異常型別Type2
}複製程式碼
關鍵詞 try 後面的大括號區域為可能發生異常的程式碼,稱為監控區。當丟擲異常或者出現執行時異常,然後由 Java 執行時系統巡展匹配 catch 子句。若有匹配的catch子句,則執行其異常處理程式碼,try-catch語句結束。
匹配規則:丟擲的異常屬於 catch 語句捕獲異常類及子類,則匹配成功。
需要注意的是:一旦某個catch捕獲到匹配的異常型別,將進入異常處理程式碼。一經處理結束,就意味著整個try-catch語句結束。其他的catch子句不再有匹配和捕獲異常型別的機會,並且 try 裡面如果有沒有走完的語句也會自動跳過。
- try-catch-finally
這個很簡單,無論 try-catch 是正常結束還是發生異常捕獲,都會執行 finally 語句,如果在 finally 之前發生了 return,那麼 finally 語句塊會在 retrun 之前執行。
小結:
try 塊:用於捕獲異常。其後可接零個或多個catch塊,如果沒有catch塊,則必須跟一個finally塊。
catch 塊:用於處理try捕獲到的異常。
finally 塊:無論是否捕獲或處理異常,finally塊裡的語句都會被執行。當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回之前被執行。
finally 如果有 return 會覆蓋 catch 裡的throw,同樣如果 finally 裡有 throw 會覆蓋 catch 裡的 return。
try-catch-finally 規則
1.必須在 try 後面新增 catch 或 finally。可以同時存在,catch 塊也可以有多個,但是 catch 必須放在 try 塊或者 catch 塊後面。
2.try-catch-finally 可以巢狀
3.在 try-catch-finally 結構中,可以重新丟擲異常
4.除非 finally 中丟擲異常或者 JVM 停止執行,否則都會執行 finally 語句
try-catch-finally 語句執行順序
1.當 try 沒有捕獲到異常時:try 語句正常執行,程式跳過 catch 語句,執行 finally。
2.當 try 捕獲到異常時,catch 沒有匹配上異常,則交個 JVM 處理,finally 語句塊還是會被執行,但是 finally 之後的語句不會被執行了。
3.當 try 捕獲到異常,catch 匹配上了,則按照 try-catch-finally 的正常順序執行,但是 try 裡面異常語句之後的語句不會被執行。
丟擲異常
任何 Java 程式碼都可以丟擲異常,語句通過 throw 丟擲、方法則是 throws。
為什麼要 throw 異常?
如果一個方法可能會出現異常,但是沒有能力處理這種又一次,則可以在方法宣告處用 throws 語句來丟擲。
throws 語句可以同時宣告丟擲多個異常,語法格式如下
void methodName()throws Exception1,Exception2,Exception3{
}複製程式碼
就醬紫。此時這個方法就不用對方法重點問這些異常做任何處理,但是呼叫這個方法的語句必須要處理,要麼繼續拋,要麼 try-catch。
Throws 丟擲規則
1.如果是不可查異常,那麼不用宣告,編譯仍然可以通過,但是執行時會被JVM 丟擲
2.如果是可查異常,要麼 try-catch,要麼 throws,否則編譯無法通過。
3.當丟擲了異常時,該方法的呼叫者才會處理或者重新丟擲該異常。
4.呼叫 throws 異常的方法,必須 try-catch 呼叫語氣,且 catch 的異常必須是丟擲異常的同類或者父類。
Throw 丟擲異常
這個真沒什麼好說的了,想拋或者需要拋,就直接呼叫就行了,注意 throw 後面只能接 Throwable 的子類。
Throwable 類常用方法
這個本應該在開篇就講的,但是好像都不怎麼常用,所以就放在這裡查漏補缺吧
返回值 | 方法名 | 方法說明 |
---|---|---|
Throwable | fillInStackTrace() | 在異常堆疊跟蹤中填充 |
Throwable | getCause | 返回 Throwable 的 cause |
String | getLocalizedMessage | 建立此 Throwable 的本地化描述 |
String | getMessage | 返回此 Throwable 的詳細訊息字串 |
StackTraceElement[] | getStackTrace() | 提供程式設計訪問由printStackTrace 輸出的堆疊跟蹤資訊 |
Throwable | initCause | 將此 throwable 的 cause 初始化為指定值 |
void | printStackTrace | 將此 Throwable 及其追蹤輸出至標準錯物流 |
String | toString | 返回此 Throwable 的簡短描述 |
Java 常見的異常
執行時異常
1.ArrayIndexOutOfBoundException
陣列索引越界異常。
2.ArithmeticException
算術條件異常,如除數為0
3.NullPointException
空指標異常。
4.ClassNotFoundException
找不到類異常。
5.NegativeArraySizeException
陣列長度為負異常
6.ArrayStoreException
陣列中包含不相容的值丟擲異常
7.SecurityException
安全性異常
8.IllegalArgumentException
非法引數異常IOException
1.IOException
操作輸入流和輸出流可能出現的異常
2.EOFException
檔案已結束異常
3.FileNotFoundException
檔案未找到異常
- 其他
1.ClassCastException
型別轉換異常類
2.ArrayStoreException
陣列中包含不相容的值丟擲的異常
3.SQLException
運算元據庫異常類
4.NoSuchFieldException
欄位未找到異常
5.NoSuchMethodException
方法未找到丟擲的異常
6.NumberFormatException
字串轉換為數字丟擲的異常
7.StringIndexOutOfBoundsException
字串索引超出範圍丟擲的異常
8.IllegalAccessException
不允許訪問某類異常
9.InstantiationException
當應用程式試圖使用Class類中的newInstance()方法建立一個類的例項,而指定的類物件無法被例項化時,丟擲該異常
自定義異常
使用Java 內建的異常類可以描述在程式設計中出現的大部分情況,除此之外,使用者還可以自定義異常。自定義異常類,只需要繼承 Exception 即可。
在程式中使用自定義異常類,大體可分為以下幾個步驟。
1.建立自定義異常類。
2.在方法中通過throw關鍵字丟擲異常物件。
3.如果在當前丟擲異常的方法中處理異常,可以使用try-catch語句捕獲並處理;否則在方法的宣告處通過throws關鍵字指明要丟擲給方法呼叫者的異常,繼續進行下一步操作。
4.在出現異常方法的呼叫者中捕獲並處理異常。
練習
異常差不多就到這裡吧,做完這個小練習,如果能做對的話就差不多了,如果做不對,那再回過頭去重新讀一遍這篇文章。
//執行語句
TestException testException1 = new TestException();
try {
testException1.testEx();
} catch (Exception e) {
e.printStackTrace();
}
//異常類
public class TestException {
public TestException() {
}
boolean testEx() throws Exception {
boolean ret = true;
try {
ret = testEx1();
} catch (Exception e) {
Log.e("sss___sss","testEx, catch exception");
ret = false;
throw e;
} finally {
Log.e("sss___sss","testEx, finally; return value=" + ret);
return ret;
}
}
boolean testEx1() throws Exception {
boolean ret = true;
try {
ret = testEx2();
if (!ret) {
return false;
}
Log.e("sss___sss","testEx1, at the end of try");
return ret;
} catch (Exception e) {
Log.e("sss___sss","testEx1, catch exception");
ret = false;
throw e;
} finally {
Log.e("sss___sss","testEx1, finally; return value=" + ret);
return ret;
}
}
boolean testEx2() throws Exception {
boolean ret = true;
try {
int b = 12;
int c;
for (int i = 2; i >= -2; i--) {
c = b / i;
Log.e("sss___sss","i=" + i);
}
return true;
} catch (Exception e) {
Log.e("sss___sss","testEx2, catch exception");
ret = false;
throw e;
} finally {
Log.e("sss___sss","testEx2, finally; return value=" + ret);
return ret;
}
}
}複製程式碼
求以上語句的答應結果~
自己先讀一遍,再來對答案哦~
sss___sss: i=2
sss___sss: i=1
sss___sss: testEx2, catch exception
sss___sss: testEx2, finally; return value=false
sss___sss: testEx1, finally; return value=false
sss___sss: testEx, finally; return value=false複製程式碼
好了,異常機制的學習就到這裡吧。