前言
Java的基本理念是結構不佳的代碼將不能運行。
異常(不期而至的各種狀況)是程序中地一些錯誤,並不是所有的錯誤都是異常,但是有時候錯誤可以避免的。異常是一個事件,它發生在程序運行期間,干擾了正常的指令流程。
一、Java中的異常層次結構
Error類(專門用來處理嚴重影響程序運行的錯誤,無法處理的錯誤,好比絕症): Java運行時系統的內部錯誤和資源耗盡錯誤。應用程序不應該拋出這種類型的對象。如果出現了這樣的內部錯誤,除了通告給用戶,並盡力使程序安全地終止之外,再也無能爲力。
Exception(好比感冒、闌尾炎): 表示異常,異常產生後程序員可以通過代碼的方式糾正,使程序繼續運行,必須要處理的。這種異常分爲兩大類:運行時異常和非運行時異常。
RuntimeException(運行異常): 由程序邏輯錯誤導致的異常。Java編譯器不會檢查它的,編譯會通過。
非運行時異常(編譯錯誤):是RuntimeException以外的異常,類型上都屬於Exception類及其子類。從程序語法上說是必須進行處理的異常,否則,編譯不通過。
非受查異常(繼承於Error、RuntimeException類的所有異常): 即不檢查異常,程序員也無需寫異常處理的代碼,自動捕獲異常。
Throwable類中的常見方法:
public void printStackTrace(){///打印異常的詳細信息
}///包含異常的類型、異常的原因、異常出現的位置,再開發和調試階段,都得使用printStackTrace
public String getMessage(){///獲取發生異常得原因
}///提示給用戶得時候,就提示錯誤原因
public String toString(){///獲取異常得類型和異常描述信息
}
二、常見的Exception類(運行時和非運行時)異常
三、異常的處理方式
3.1 拋出異常(throw)
在Java中,提供了一個throw關鍵字,且其用在方法內 ,它用來拋出一個指定得異常對象。
步驟: 創建一個異常對象;需要將這個異常對象告知給調用者。
格式: throw new 異常類名(參數)
public class Main {
public static void main(String [] args){
int [] arr = {1,3,2,4};
int n = 4;
int x = getElment(arr,n);
System.out.println(x);
}
public static int getElment(int []arr,int n){
if(n<0||n>arr.length-1){
throw new ArrayIndexOutOfBoundsException("數組越界了");
}
int x = arr[n];
return x;
}
}
控制檯輸出結果(拋出異常)如下:
3.2 聲明異常(throws)
聲明異常: 將問題標識出來,報告給調用者。如果方法內拋出編譯異常,而沒有捕獲處理,那麼必須通過throws進行聲明,讓調用者去處理。
關鍵字throws 運用於方法聲明之上,用於表示當前方法不處理異常,而是提醒該方法的調用者來處理異常(拋出異常)。通俗地,用在聲明方法時,表示該方法可能要拋出異常。
public void Method() throws Exception1,...{///可以聲明多個異常
....
}
3.3 throws和throw的區別
關鍵字 | 位置不同 | 作用不同 | 是否出現異常 |
---|---|---|---|
throw | 用在方法頭(函數體) | 表示拋出異常,由方法體內的語句處理,所以它是拋出一個異常實例 | 執行throw就是一定拋出了某種異常 |
throws | 用在方法聲明後面(函數頭) | 表示再拋出異常 ,使它的調用者知道要捕獲這個異常 | 表示出現異常的一種可能性,並不一定會發生這些異常 |
共性: 都是消極處理異常的方式(這裏的消極並不是說這種方式不好),只是拋出或者可能拋出異常,但是不會由函數去處理異常,真正的處理異常由函數的上層調用處理。
3.4 捕獲異常(try…catch)
如果異常出現的話,會立刻終止程序,所以我們得處理異常。一個方法所能捕捉的異常,一定是Java代碼在某處所拋出的異常。簡單地說,異常總是先被拋出,後被捕捉。
方法處理異常的兩種情況:
1、該方法不處理,而是聲明拋出,由該方法的調用者來處理
2、在方法中使用try-catch 的語句塊來處理異常
try{///該代碼塊中編寫可能產生異常的代碼
...
}catch(異常類型 e){///用來進行某種異常的捕獲,實現對捕獲到的異常進行處理
處理異常的代碼
//記錄日誌/打印日常信息/繼續拋出異常
}
try…catch語句: 如果你在方法內部拋出了異常(或在方法內部調用的其他方法拋出了異常),這個方法將拋出異常的過程中結束。這是我們不希望的,所以try塊內將可能發生異常的代碼包起來,稱爲監控區域。Java方法在運行過程中出現異常,則創建異常對象(catch參數所定義的異常)。將異常拋出監控區域之外,由Java運行系統試圖尋找匹配的catch子句以捕獲異常(異常處理程序)。若有匹配的catch子句,則運行其異常處理代碼,try-catch語句結束。
注意:如果在try塊的內部,不同的方法調用可能會產生類型相同的異常,你只需提供一個針對此類型的異常處理程序。
匹配的原則: 如果拋出的異常對象屬於catch子句的異常類,或者屬於該異常類的子類,則認爲生成的異常對象與catch塊捕獲的異常類型相匹配。
3.5 finally代碼塊
有一些特定的代碼無論異常是否發生,都需要執行。另外,因爲異常會引發程序跳轉,導致有些語句執行不到。而finally代碼塊中存放的代碼都是一定被執行的。
try{
//可能發生異常的程序代碼
}catch(異常類型 e){
//捕獲並處理try拋出的異常類型
}finally{
//無論異常是否發生,都將執行
}
try…catch…finally執行順序:
資料來自於https://blog.csdn.net/hguisu/article/details/6155636?utm_source=app&from=singlemessage
1、當try沒有捕獲到異常時:
try語句塊中的語句逐一被執行,程序跳過catch語句塊,執行finally語句塊和其後的語句。
2、當try捕獲到異常時:
· catch語句塊裏沒有處理此異常的情況:此異常將會拋給JVM處理,finally語句還是會被執行,但finally語句塊後的語句不會被執行。
· catch語句塊裏出現處理此異常的情況:當執行某一條語句出現異常時,程序將跳到catch語句塊,並與catch語句塊逐一匹配,找到與之對應的處理程序,其他的catch將不會被執行,而try語句塊中,出現異常之後的語句也不會被執行,catch語句塊執行完後,執行finally語句塊,最後執行其他語句。
什麼時候的代碼塊必須最終執行?
當我們在try語句塊中打開了一些物理資源(磁盤文件/網路連接/數據庫連接等),我們都得在使用完後,最終關閉打開得資源。
finally塊不會被執行的4種特殊情況:
1、在finally語句塊發生異常。
2、前面代碼使用了System.exit()退出程序。
3、程序所在的線程死亡
4、關閉CPU
finally的其他用法
☞ 當在try塊或catch塊中遇到return語句時,finally將在返回之前被執行:
public class Main {
public static void main(String [] args){
System.out.println(f(2));//輸出0
}
public static int f(int x){
try{
int n = x*x;
return n;
}finally{
if(x==2) return 0;
}
}
}
☞ 耦合try/catch和try/finally語句塊
///如果finally子句中出現錯誤時,會報告finally子句中出現的錯誤。
InputStream in = ...;
try{///確保報告出現的錯誤
try{///確保關閉輸入流
}finally{
in.close();
}
}catch(IOException e){
...
}
3.6 異常的注意事項
對於有多個catch子句的異常程序來說,應該儘量將捕或底層異常類的catch子句(如:子類)放在前面,同時儘量將捕獲相對高層的異常類的catch子句(如:父類)放在後面。否則,捕獲底層異常類的catch子句將可能會被屏蔽。
多個異常使用捕獲情況:
1、多個異常分別處理
2、多個異常一次捕獲,多次處理
3、多個異常一次捕獲一次處理。
一般我們是使用一次捕獲多次處理的方式。
四、自定義異常
爲什麼需要自定義異常類:
上面所學的異常,我們會發現都是JDK內部定義好的,但是實際開發中也會出現很多異常,這些異常很可能在JDK中沒有定義過的,例如年齡的負數問題,考試問題,負數問題,那麼能不能自己定義異常呢?
什麼時自定義異常類:在開發中根據自己的業務異常情況來定義異常類。
五、參考資料
1.https://www.cnblogs.com/xiohao/p/3547443.html
2.https://blog.csdn.net/hguisu/article/details/6155636?utm_source=app&from=singlemessage
3.黑馬程序員