????????續接上一講
目錄
一、異常的處理(續)
1、異常的捕獲-try-catch捕獲并處理異常
1.1關于異常的處理方式
2、finally
3、異常的處理流程
二、自定義異常類
1、實現自定義異常類
一、異常的處理(續)
1、異常的捕獲-try-catch捕獲并處理異常
????????throws對異常并沒有真正處理,而是將異常報告給拋出異常方法的調用者,由調用者處理。如果真正要對異常進行處理,就需要try-catch。
語法格式:
try{// 將可能出現異常的代碼放在這?
}catch(要捕獲的異常類型e){// 如果try中的代碼拋出異常了,此處catch捕獲時異常類型與try中拋出的異常類型?致時,或者是try中拋出異常的基類時,就會被捕獲到 // 對異常就可以正常處理,處理完成后,跳出try-catch結構,繼續執?后序代碼
}[catch(異常類型 e){// 對異常進?處理
}finally{// 此處代碼?定會被執?到
}]// 后序代碼
// 當異常被捕獲到時,異常就被處理了,這?的后序代碼?定會執?
// 如果捕獲了,由于捕獲時類型不對,那就沒有捕獲到,這?的代碼就不會被執?注意:
1. []中表?可選項,可以添加,也可以不?添加
2. try中的代碼可能會拋出異常,也可能不會
例:讀取配置文件,如果配置文件名字不是指定名字,拋出異常,調用者進行異常處理
public class Config {File file;public void openConfig(String filename) throws FileNotFoundException{if(!filename.equals("config.ini")){throw new FileNotFoundException("配置?件名字不對");}// 打開?件 }public void readConfig(){}public static void main(String[] args) {Config config = new Config();try {config.openConfig("config.txt");System.out.println("?件打開成功");} catch (IOException e) {// 異常的處理?式//System.out.println(e.getMessage()); // 只打印異常信息 //System.out.println(e); // 打印異常類型:異常信息 e.printStackTrace(); // 打印信息最全?}// ?旦異常被捕獲處理了,此處的代碼會執? System.out.println("異常如果被處理了,這?的代碼也可以執?");}}
1.1關于異常的處理方式
異常的種類有很多,我們要根據不同的業務場景來決定:
(1)對于比較嚴重的問題(如算錢),應該直接讓程序崩潰,減小損失
(2)對于不太嚴重的問題,可以記錄錯誤日志,通過監控報警程序及時通知告知程序猿
(3)對于可能會恢復的問題(網絡相關場景),可以嘗試重試
????????在我們當前的代碼中采取的是經過簡化的第?種方式.我們記錄的錯誤日志是出現異常的方法調用信息,能很快速的讓我們找到出現異常的位置.以后在實際工作中我們會采取更完備的方式來記錄異常信息
#注:
(1)try塊內拋出異常位置之后的代碼將不會被執行
(2)如果拋出異常類型與catch時異常類型不匹配,即異常不會被成功捕獲,也就不會被處理,繼續往外拋,直到JVM收到后中斷程序----異常是按照類型來捕獲的
public static void main(String[] args) {try {int[] array = {1,2,3};System.out.println(array[3]); // 此處會拋出數組越界異常 }catch (NullPointerException e){ // 捕獲時候捕獲的是空指針異常--真正的異常?法被捕獲到 e.printStackTrace();}System.out.println("后續代碼");}Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3at day20210917.ArrayOperator.main(ArrayOperator.java:24)
(3)try中可能會拋出多個不同的異常對象,則必須用多個catch來捕獲----即多種異常,多次捕獲
public static void main(String[] args) {int[] array = {1, 2, 3};try {System.out.println("before");// array = null;System.out.println(array [100]);System.out.println("after");} catch (ArrayIndexOutOfBoundsException e) {System.out.println("這是個數組下標越界異常");e.printStackTrace();} catch (NullPointerException e) {System.out.println("這是個空指針異常");e.printStackTrace();}System.out.println("after try catch");}
????????如果多個異常完全相同,也可以這樣寫:
catch (ArrayIndexOutOfBoundsException | NullPointerException e) {...}
????????如果異常之間具有父子關系,?定是子類異常在前catch,父類異常在后catch,否則語法錯誤:
public static void main(String[] args) {int[] arr = {1, 2, 3};try {System.out.println("before");arr = null;System.out.println(arr[100]);System.out.println("after");} catch (Exception e) { // Exception可以捕獲到所有異常 e.printStackTrace();}catch (NullPointerException e){ // 永遠都捕獲執?到 e.printStackTrace();}System.out.println("after try catch");}Error:(33, 10) java: 已捕獲到異常錯誤java.lang.NullPointerException
(4)可以通過?個catch捕獲所有的異常,即多個異常,?次捕獲(不推薦)
public static void main(String[] args) {int[] arr = {1, 2, 3};try {System.out.println("before");arr = null;System.out.println(arr[100]);System.out.println("after");} catch (Exception e) {e.printStackTrace();}System.out.println("after try catch");}
????????由于Exception類是所有異常類的父類.因此可以用這個類型表示捕捉所有異常.
#注:catch進行類型匹配的時候,不光會匹配相同類型的異常對象,也會捕捉目標異常類型的子類對 象
2、finally
????????在寫程序時,有些特定的代碼,不論程序是否發?異常,都需要執行,比如程序中打開的資源:網絡連接、數據庫連接、IO流等,在程序正常或者異常退出時,必須要對資源進進行回收。另外,因為異常會引發程序的跳轉,可能導致有些語句執行不到,finally就是用來解決這個問題的。
語法格式:
try{// 可能會發?異常的代碼
}catch(異常類型 e){// 對捕獲到的異常進?處理
}finally{// 此處的語句?論是否發?異常,都會被執?到
}// 如果沒有拋出異常,或者異常被捕獲處理了,這?的代碼也會執?
public static void main(String[] args) {try{int[] array = {1,2,3};array [100] = 10;array [0] = 10;}catch (ArrayIndexOutOfBoundsException e){System.out.println(e);}finally {System.out.println("finally中的代碼?定會執?");}System.out.println("如果沒有拋出異常,或者異常被處理了,try-catch后的代碼也會執?");}
#注:finally中的代碼?定會執行的,?般在finally中進行?些資源清理的掃尾工作,如果異常被捕獲,那么將不會繼續執行后續的內容,而是直接走完當前捕獲異常的catch后直接走finally(除了finally其他的都不執行)
????????finally 執行的時機是在方法返回之前(try或者catch中如果有return會在這個return之前執行 finally). 但是如果finally 中也存在return語句,那么就會執行finally中的return,從而不會執行到try 中原有的return.
?????????般我們不建議在finally中寫return(會被編譯器當做?個警告)
3、異常的處理流程
關于“調用棧”:
????????方法之間是存在相互調用關系的,這種調用關系我們可以"調用棧"來描述.在JVM中有?塊內存空間稱為"虛擬機棧",專門存儲方法之間的調用關系.當代碼中出現異常的時候,我們就可以使用 e.printStackTrace(); 的方式查看出現異常代碼的調用棧.
????????如果本方法中沒有合適的處理異常的方式,就會沿著調用棧向上傳遞
public static void main(String[] args) {try {func();} catch (ArrayIndexOutOfBoundsException e) {e.printStackTrace();}System.out.println("after try catch");}public static void func() {int[] array = {1, 2, 3};System.out.println(array[100]);}//結果java.lang.ArrayIndexOutOfBoundsException: Index 100 out of bounds for length 3at LibrarySystem.func(LibrarySystem.java:107)at LibrarySystem.main(LibrarySystem.java:98)after try catch
????????如果向上?直傳遞都沒有合適的方法處理異常,最終就會交給JVM處理,程序就會異常終止(和我們最開始未使用trycatch時是?樣的)
異常處理流程總結:
(1)程序先執行try中的代碼
(2)如果try中的代碼出現異常,就會結束try中的代碼,看和catch中的異常類型是否匹配
(3)如果找到匹配的異常類型,就會執行catch中的代碼
(4)如果沒有找到匹配的異常類型,就會將異常向上傳遞到上層調用者
(5)無論是否找到匹配的異常類型,finally中的代碼都會被執行到(在該方法結束之前執行)
(6)如果上層調用者也沒有處理的了異常,就繼續向上傳遞
(7)?直到main方法也沒有合適的代碼處理異常,就會交給JVM來進行處理,此時程序就會異常終止
二、自定義異常類
????????Java 中雖然已經內置了豐富的異常類,但是并不能完全表示實際開發中所遇到的?些異常,此時就需要維護符合我們實際情況的異常結構
例如,我們實現?個用戶登陸功能:
public class LogIn {private String userName = "admin";private String password = "123456";public void loginInfo(String userName, String password) {if (!this.userName.equals(userName)) {System.out.println("??名錯誤!");return;}if (!this.password.equals(password)) {System.out.println("密碼錯誤!");return;}System.out.println("登陸成功");}public static void main(String[] args) {LogIn logIn = new LogIn();logIn.loginInfo("admin111", "123456");}}
????????此時我們在處理用戶名密碼錯誤的時候可能就需要拋出兩種異常.我們可以基于已有的異常類進行擴展 (繼承),創建和我們業務相關的異常類
1、實現自定義異常類
具體方式:
(1)自定義異常類,然后繼承自Exception或者RunTimeException
(2)實現?個帶有String類型參數的構造方法,參數含義:出現異常的原因
class UserNameException extends Exception {public UserNameException(String message) {super(message);}}class PasswordException extends Exception {public PasswordException(String message) {super(message);}}
此時我們的LogIn代碼可以改成:
public class LogIn {private String userName = "admin";private String password = "123456";public void loginInfo(String userName, String password) throws UserNameException,PasswordException{if (!this.userName.equals(userName)) {throw new UserNameException("用戶名錯誤!");}if (!this.password.equals(password)) {throw new PasswordException("用戶名錯誤!");}System.out.println("登陸成功");}public static void main(String[] args) {try {LogIn login = new LogIn();login.loginInfo("admin", "123456");} catch (UserNameException e) {e.printStackTrace();} catch (PasswordException e) {e.printStackTrace();}}}
#注:
(1)自定義異常通常會繼承自Exception或者RuntimeException
(2)繼承自Exception的異常默認是受查異常
(3)繼承自RuntimeException的異常默認是非受查異常