目錄
例外的概念與體系結構
概念
常見的例外
例外的體系結構
例外的分類
例外的處理
防御式編程
例外的拋出
例外的捕獲
例外宣告(使用關鍵字throws)
try-catch捕獲處理
關鍵字finally
例外的處理流程
例外的概念與體系結構
概念
例外簡單理解就是不正常,Java中的例外就是程式在執行程序中出現不正常的行為稱之為例外
常見的例外
1. 算數例外(常見的是除數為0)
System.out.println(10/0);
//執行結果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
2.陣列越界例外
int[] arr = {1,2,3};
System.out.println(arr[10]);
//執行結果:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
3. 空指標例外
int[] arr = null;
System.out.println(arr.length);
//執行結果:
Exception in thread "main" java.lang.NullPointerException
從上述例子可以看出:不同的例外對應的例外描述不同,也就是每個例外都有對應的類來進行描述,
例外的體系結構
例外的種類非常的多,為了易于區分和管理,Java內部維護了一個例外的體系結構,見下圖:

對上面圖進行說明:
1.Throwable:是例外的頂層類,其派生出兩個子類:Error和Exception
2. Error:指的是Java虛擬機無法解決的問題,此類問題比較嚴重,例如:JVM內部錯誤,資源耗盡等,典型代表:StackOverflowError和OutOfMemoryError,該類例外一但發生,就無法解決,就像人得了癌癥,
3.Exception:該類例外產生后可由程式員進行分析及處理,使得程式繼續運行,就比如我們人感冒咳嗽一樣,
例外的分類
因為例外發生的時機是不一樣的,我們根據此將例外分為:編譯時例外,運行時例外,
1. 編譯時的例外
它指的是在程式編譯期間發生的例外,也可稱之為受檢查例外(Checked Exception)
2. 運行時的例外
它指的是在程式執行期間發生的例外,也稱之為非受檢查例外(Unchecked Exception)
RuntimeException以及其子類對應的例外都稱為運行時例外,例如:NullPointerException等
注意:編譯時出現的語法錯誤不能稱為例外,比如將某個單詞拼寫錯誤,此時編譯程序中就會出錯,這是“編譯期”出錯,而運行時例外指的是,編譯已經完成得到class檔案,再由JVM執行程序中出現的錯誤,
例外的處理
防御式編程
1.LBYL:Look Before You Leap :指的是在對代碼進行操作之前做充分的檢查,即:事前防御
例:
boolean ret = false;
ret = 登錄游戲();
if(!ret){
處理登錄游戲錯誤;
return;
}
ret = 開始匹配();
if(!ret){
處理匹配錯誤;
return;
}
ret = 游戲確認();
if(!ret){
處理游戲確認錯誤;
return;
}
......
這種處理方式的缺陷:正確的流程和處理錯誤的流程混在一起了,整體比較混亂,不易閱讀
2.EAFP:It's Easier to Ask Forgiveness than Permission.指的是先進行操作,遇到了問題在處理,即:事后認錯型
try{
登錄游戲();
開始匹配();
游戲確認();
......
}catch(登錄游戲例外){
處理登錄游戲例外;
}catch(開始匹配例外){
處理開始匹配例外;
}catch(游戲確認例外){
處理游戲確認例外;
}
......
優勢:正常和錯誤流程分開,代碼清晰,易于閱讀理解
例外處理的5個主要關鍵字:throw try catch final throws
例外的拋出
在寫程式時,如果程式出現錯誤,就要將錯誤的資訊告訴給呼叫者,在Java中,借助throw關鍵字,拋出一個指定的例外物件,將錯誤資訊告知給呼叫者,具體語法如下:
throw new XXXException("例外產生的原因");
舉一個陣列傳參的例子進行說明:
public static int getElement(int[] array,int index){
if(null==array){
throw new NullPointerException("傳遞陣列為null");
}
if(index<0||index>=array.length){
throw new ArrayIndexOutOfBoundsException("傳遞的陣列下標越界");
}
return array[index];
}
public static void main(String[] args) {
int[] array = {1,2,3};
getElement(array,3);
}
運行結果:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 傳遞的陣列下標越界
注意事項:
1.throw必須寫在方法體內部
2. 拋出的物件必須是Exception或者Exception的子類物件
3. 如果拋出的是RuntimeException或者它的子類,可以不用處理,JVM會幫我們處理
4. 如果拋出的是編譯時例外,用戶必須處理,否則無法通過編譯
5. 例外一旦拋出,其后的代碼就不會執行
例外的捕獲
例外的捕獲也就是例外的具體處理方式,主要有兩種:用throws進行例外宣告以及try-catch捕獲處理,
例外宣告(使用關鍵字throws)
當程式拋出編譯時例外時而且用戶不想處理該例外,就可以借助throws將例外拋給方法的呼叫者來處理,
語法格式:
修飾符 回傳值型別 方法名(引數串列) throws 例外型別1,例外型別2...{ }
注意事項:
1. throws必須跟在方法的引數串列之后
2. 宣告的例外必須是Exception或者其子類
3. 方法內部如果拋出多個例外,throws之后跟多個例外型別,之間用逗號隔開,如果拋出多個例外型別具有父子關系,則直接宣告父類即可
4.呼叫拋出例外的方法時,呼叫者必須對該例外進行處理,或者繼續使用throws拋出
try-catch捕獲處理
throws對例外并沒有處理,而是交給呼叫者處理,如果要對例外真正進行處理,就需要用try-catch
語法格式:
try{ //這里為可能出現例外的代碼 }catch(要捕獲的例外型別 e){ //當拋出例外與catch捕獲例外型別相同時,例外就會被捕獲到 //對例外進行處理完后,會跳出try-catch,繼續執行后序代碼 }[catch(例外型別 e){ //對例外處理 }finally{ //此處代碼一定會被執行 }] //后序代碼,例外被捕獲處理了,后序代碼就一定會執行 //例外沒有捕獲到,后序代碼就不會執行注意:[ ]中表示可選項,可以添加,也可以不添加
關于例外的處理方式:
1.例外的種類非常多,我們要根據不同的場景環境來決定
2. 對于更嚴重的問題(例如堆疊相關場景),應該讓程式直接崩潰,防止造成更嚴重的后果
3.對于不太嚴重的問題,可以記錄錯誤日志,并通過監控報警及時通知程式員
4. 對于可能會恢復的問題(網路相關場景),可嘗試進行重試
注意事項:
1. try塊內拋出例外位置之后的代碼將不會被執行
2. 如果拋出例外型別與catch內的例外型別不匹配,即例外不會被成功捕獲,也不會被處理,繼續往外拋,直到JVM收到后中斷程式---例外是按照型別來捕獲的
3. try中可能會拋出多種型別例外,則必須用多個catch來捕獲多種型別
4. 如果例外之間具有父子關系,一定是子類例外的catch在前,父類例外的catch在后,否則語法錯誤
由于Exception是所有例外類的父類,所以可以用這個型別表示捕捉所有例外
關鍵字finally
在寫代碼時,有些特定的代碼無論程式是否發生例外都需要執行,比如程式中的打開資源,網路連接,資料庫連接,IO等,在程式正常或者例外退出時,必須要對資源進行回收,另外,因為例外會引發程式的跳轉,可能導致有些陳述句執行不到,finally就是用來解決這一問題的,
語法格式:
try{
//可能發生例外的代碼
}catch(例外型別 e){
//對捕獲的例外進行處理
}finally{
//這里的代碼無論例外是否被捕獲到,都會執行
}
//如果沒有例外或者例外被捕獲處理了,這里后序的代碼也執行
舉陣列的例子進行說明:
public static void main(String[] args) {
try{
int[] array = {1,2,3};
array[10] = 10;
array[0] = 10;
}catch(ArrayIndexOutOfBoundsException e){
System.out.println(e);
}finally{
System.out.println("這里代碼一定執行");
}
System.out.println("如果沒有例外,或者例外被捕獲處理,這里代碼也將執行");
}
執行結果:
java.lang.ArrayIndexOutOfBoundsException: 10
這里代碼一定執行
如果沒有例外,或者例外被捕獲處理,這里代碼也將執行
這里存在了一個問題:既然finally和try-catch-finally后的代碼都會執行,為什么還要有finally的?看完下面的代碼,這個問題那就會迎刃而解,
public static int getData(){
Scanner sc = null;
try{
sc = new Scanner(System.in);
int data = sc.nextInt();
return data;
}catch(InputMismatchException e){
e.printStackTrace();
}finally{
System.out.println("finally中的代碼");
}
System.out.println("try-catch-finally之后的代碼");
if(null!=sc){
sc.close();
}
return 0;
}
public static void main(String[] args) {
int data = getData();
System.out.println(data);
}
輸入:10
運行結果:
finally中的代碼
10
上述程式,正常輸入,return data后,方法就結束了,try-catch-finally后的代碼根本沒有執行,即輸入流沒有釋放,造成資源泄露,
注意:finally中的代碼一定會執行的,一般在finally中進行一些資源清理的掃尾作業,
如果finally中也存在return陳述句,則執行finally中的return陳述句,就不會執行try中的return陳述句
例外的處理流程
1. 程式先執行try中的代碼
2. 如果try中的代碼出現例外,就會結束try中的代碼,與catch中的例外型別是否匹配
3. 如果找到匹配的例外型別,就會執行catch中的代碼
4.如果沒有找到匹配的型別,就會將例外向上傳遞到上層呼叫者
5. 無論是否找到匹配的例外型別,finally中的代碼都會被執行到(在該方法結束之前執行)
6.如果上層呼叫者也沒有處理例外,就會繼續向上傳遞,一直到main方法也沒有合適的代碼處理例外,就會交給JVM來進行處理,此時程式就會例外終止
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/346070.html
標籤:java
上一篇:命令列倒數計時器跳過一秒
下一篇:將證書檔案提供到k8s中的pod
