1.什么是異常
將程序執行過程中發生的不正常行為稱為異常。
常見的異常有:算數異常,空指針異常,數組越界異常
每一種異常都有對應的類對齊描述
為了對每一種異常進行管理,Java內部實現了一個對異常的體系結構
1. Throwable:是異常體系的頂層類,其派生出兩個重要的子類, Error 和 Exception
2. Error:指的是Java虛擬機無法解決的嚴重問題,比如:JVM的內部錯誤、資源耗盡等,典型代表: StackOver?owError和OutOfMemoryError,一旦發生回力乏術。
3. Exception:異常產生后程序員可以通過代碼進行處理,使程序繼續執行。比如:感冒、發燒。我們平時所說 的異常就是Exception。
異常的分類:
1.異常分為兩類,編譯時異常(受查異常),運行時異常(非受查異常)
2.編譯時異常,即在編寫代碼時就被檢查出來的異常,如在使用clone方法時出現的異常
3.運行時異常,即在編譯時未被檢查出來的異常,在運行的時候被檢查出來的異常,如空指針異常,數組越界異常。(RuntimeExcption以及其子類都被稱為運行時異常)
2.異常的處理
2.1防御式編程
事前防御型:在運行代碼前,對其進行檢查
boolean ret = false;
ret = 登陸游戲();
if (!ret) {處理登陸游戲錯誤;return;
}
ret = 開始匹配();
if (!ret) {處理匹配錯誤;return;
}
ret = 游戲確認();
if (!ret) {處理游戲確認錯誤;return;
}
ret = 選擇英雄();
if (!ret) {處理選擇英雄錯誤;return;
}
這種方式的代碼,缺點很明顯缺陷:就是正常流程和錯誤處理流程代碼混在一起, 代碼整體顯的比較混亂。
事后認錯型:就是先操作,遇到問題再處理
try {登陸游戲();開始匹配();游戲確認();選擇英雄();載入游戲畫面();...
} catch (登陸游戲異常) {處理登陸游戲異常;
} catch (開始匹配異常) {處理開始匹配異常;
} catch (游戲確認異常) {處理游戲確認異常;
} catch (選擇英雄異常) {處理選擇英雄異常;
} catch (載入游戲畫面異常) {處理載入游戲畫面異常;
}
這種方式的異常處理,將正常流程和錯誤處理流程代碼分開,使代碼清晰明了,方便程序員理解
在Java中,異常處理主要的5個關鍵字:throw、try、catch、?nal、throws。
2.2異常的拋出
在Java中,我們在編寫程序時,如果程序出現錯誤就需要將錯誤的信息告知調用者
拋出異常的格式:
throw new XXXXXException(“異常產生的原因”);
//XXXXXException為此異常對應的類
數組越界異常的拋出
要求:實現一個方法,獲取數組任意下標的元素
public class T1 {public static void main(String[] args) {
int []arr={0,1,2,3,4,5,6,7,8,9};System.out.println(getElement(arr,5));System.out.println(getElement(arr,-1));}public static int getElement(int[] array, int index) {if(array==null){throw new NullPointerException("數組為空");}if (index<0||index>=array.length){throw new ArrayIndexOutOfBoundsException("數組越界異常");}return array[index];}}
1. throw必須寫在方法體內部
2. 拋出的對象必須是Exception 或者 Exception 的子類對象
3. 如果拋出的是 RunTimeException 或者 RunTimeException 的子類,則可以不用處理,直接交給JVM來處理
4. 如果拋出的是編譯時異常,用戶必須處理,否則無法通過編譯
5. 異常一旦拋出,其后的代碼就不會執行
2.3異常的捕獲
異常的捕獲,也就是異常的具體處理方式,主要有兩種:異常聲明throws 以及 try-catch捕獲處理。
異常聲明throws:
處在方法聲明時參數列表之后,當方法中拋出編譯時異常,用戶不想處理該異常,此時就可以借助throws將異常拋 給方法的調用者來處理。即當前方法不處理異常,提醒方法的調用者處理異常。
語法格式:
修飾符 ?返回值類型 ? 方法名(參數列表) throws 異常類型1,異常類型2...{}
注意:
1. throws必須跟在方法的參數列表之后
2. 聲明的異常必須是 Exception 或者 Exception 的子類
3. 方法內部如果拋出了多個異常,throws之后必須跟多個異常類型,之間用逗號隔開,如果拋出多個異常類型 具有父子關系,直接聲明父類即可。
4. 調用聲明拋出異常的方法時,調用者必須對該異常進行處理,或者繼續使用throws拋出
try-catch捕獲并處理:
throws對異常并沒有真正處理,而是將異常報告給拋出異常方法的調用者,由調用者處理。如果真正要對異常進行 處理,就需要try-catch。
語法格式:
try{// 將可能出現異常的代碼放在這里
}catch(要捕獲的異常類型 ?e){// 如果try中的代碼拋出異常了,此處catch捕獲時異常類型與try中拋出的異常類型一致時,或者是try中拋出異常的基類
時,就會被捕獲到// 對異常就可以正常處理,處理完成后,跳出try-catch結構,繼續執行后序代碼
}catch(異常類型 e){// 對異常進行處理
}?nally{// 此處代碼一定會被執行到
}
當異常被捕獲到時,異常就被處理了,這里的后序代碼一定會執行
如果捕獲了,由于捕獲時類型不對,那就沒有捕獲到,這里的代碼就不會被執行
注意:
1. try中的代碼可能會拋出異常,也可能不會
2.catch和finally按照需求添加,在這個try-catch()中,有多個catch()中的異常類型時,都是子類異常類在前catch(),父類異常類在后catch()。
小結:
1. try塊內拋出異常位置之后的代碼將不會被執行
2. 如果拋出異常類型與catch時異常類型不匹配,即異常不會被成功捕獲,也就不會被處理,繼續往外拋,直到 JVM收到后中斷程序----異常是按照類型來捕獲的
3. try中可能會拋出多個不同的異常對象,則必須用多個catch來捕獲----即多種異常,多次捕獲
如果多個異常的處理方式是完全相同, 也可以寫成這樣:
catch (ArrayIndexOutOfBoundsException | NullPointerException e) {... }
如果異常之間具有父子關系,一定是子類異常在前catch,父類異常在后catch,否則語法錯誤
4. 可以通過一個catch捕獲所有的異常,即多個異常,一次捕獲(不推薦),由于 Exception 類是所有異常類的父類. 因此可以用這個類型表示捕捉所有異常.
5.catch 進行類型匹配的時候, 不光會匹配相同類型的異常對象, 也會捕捉目標異常類型的子類對象. 如剛才的代碼, NullPointerException 和 ArrayIndexOutOfBoundsException 都是 Exception 的子類, 因此都能被捕獲到.
2.4finally
1.無論try-catch是否捕獲到異常,finally里面的語句都會被執行
2.?nally中的代碼一定會執行的,一般在?nally中進行一些資源清理的掃尾工作
3.?nally 執行的時機是在方法返回之前(try 或者 catch 中如果有 return 會在這個 return 之前執行 ?nally). 但是如果 ?nally 中也存在 return 語句, 那么就會執行 ?nally 中的 return, 從而不會執行到 try 中原有的 return. 一般我們不建議在 ?nally 中寫 return (被編譯器當做一個警告).
?
3.異常的處理流程
1.程序先執行 try 中的代碼 如果 try 中的代碼出現異常, 就會結束 try 中的代碼, 看和 catch 中的異常類型是否匹配.
2.如果找到匹配的異常類型, 就會執行 catch 中的代碼 如果沒有找到匹配的異常類型, 就會將異常向上傳遞到上層調用者.
3.無論是否找到匹配的異常類型, ?nally 中的代碼都會被執行到(在該方法結束之前執行).
4.如果上層調用者也沒有處理異常, 就繼續向上傳遞.
5.一直到 main 方法也沒有合適的代碼處理異常, 就會交給 JVM 來進行處理, 此時程序就會異常終止.
4.自定義異常
Java中雖然定義了許多異常,但是在各種業務中,為了滿足業務的需求需要,我們需要自定義異常來滿足業務需求
登錄系統:
我們在處理用戶名密碼錯誤的時候可能就需要拋出兩種異常. 我們可以基于已有的異常類進行擴展(繼承), 創建和我們業務相關的異常類.
class nameExpection extends Exception{public nameExpection(String message) {super(message);}
}
class passwordExpection extends Exception{public passwordExpection(String message) {super(message);}
}public class T2 {private static String name="張三";private static String password="12345";public static void login(String name1, String password1) throws nameExpection,passwordExpection{if (!name.equals(name1)){
throw new nameExpection("用戶名錯誤");}if (password.equals(password1)){throw new passwordExpection("登錄密碼錯誤");}System.out.println("登錄成功");}public static void main(String[] args) {try {login("李四","22341");} catch (nameExpection e) {e.printStackTrace();} catch (passwordExpection e) {e.printStackTrace();}}
}
具體方式:
1. 自定義異常類,然后繼承自Exception 或者 RunTimeException
2. 實現一個帶有String類型參數的構造方法,參數含義:出現異常的原因
注意事項:
1.自定義異常通常會繼承Exception 或者 RuntimeException
2.繼承Exception的異常默認是受查異常 繼承自RuntimeException 的異常默認是非受查異常.