一、Java 異常處理
編寫程序時,要在出現可能出現錯誤的時候加上檢測的代碼,如判斷分母為0,數據為空,過多的 if-else分支導致程序代碼加長,臃腫,可讀性差,因此采用異常處理機制。
Java采用的異常處理機制,是將異常處理的程序代碼集中在一起,與正常的程序代碼分開,使得程序簡潔、優雅, 並易於維護。
二、異常處理:抓拋模型
1、拋出
Java 程序的執行過程中出現異常,會生成一個異常類對象,該異常對象將被提交給 Java 運行時系統,這個過程稱為拋出(throw)異常。一旦拋出對象以后,其后的代碼就不再執行。
2、捕獲、“抓”
可以理解為異常的處理方式:① try-catch-finally; ② throws;
3、異常對象的生成
(1)由虛擬機自動生成:程序運行過程中,虛擬機檢測到程序發生了問題,如果在當前代碼中沒有找到相應的處理程序,就會在后台自動創建一個對應異常類的實例對象並拋出——自動拋出;
(2)由開發人員手動創建:Exception exception = new ClassCastException;——創建好的異常對象不拋出對程序沒有任何影響,和創建一個普通對象一樣;
4、處理異常過程
(1)當程序運行到某一句時,發生了異常,那么程序會先停下來;
(2)程序會在這句代碼處,查看原因,生成一個合理“異常對象”,然后“拋”出;
(3)JVM 會檢測這句代碼的外圍,是否有 try...catch 結構,可以“捕獲”它;
(4)如果可以捕獲,那么程序在處理完異常后,繼續下面的運行,不會崩潰;
(5)如果不能捕獲,那么會把這個異常繼續拋給“上級”,如果“上級”能處理,那么程序從“上級"處理完的代碼后面繼續運行;
(6) 如果上級也不能處理,那么繼續往上拋,一直到達JVM,那么就“崩潰”;
① 如果一個方法內拋出異常,該異常對象會被拋給調用者方法中處理。 如果異常沒有在調用者方法中處理, 它繼續被拋給這個調
方法的上層方法。 這個過程將一直繼續下去, 直到異常被處理。這一過程稱為捕獲(catch)異常。
② 如果一個異常回到main()方法, 並且main()也不處理, 則程序運行終止。
③ 程序員通常只能處理Exception, 而對Error無能為力。
5、不捕獲異常時的情況
(1)如果捕獲的是 RuntimeException 類或它的子類,這些類的異常特點是:即使沒有使用 try 和 catch 捕獲,Java 自己也能捕獲,並且編譯通過,(但運行時會發生異常使得程序運行終止)
(2)如果拋出的異常是 IOException 等類型的非運行時異常,則必須捕獲,否則編譯錯誤,也就是說,必須處理編譯時異常,將異常進行捕捉,轉化為運行時異常;
三、處理機制一:try...catch...finally
1、try
捕獲異常的第一步是用 try{} 語句塊選定捕獲異常的范圍,將可能出現的異常的代碼放在 try 語句塊中;
2、catch(ExceptionType e)
在 catch 語句塊中是對 異常對象 進行處理的代碼。每個 try 語句塊可以伴隨一個或多個 catch 語句,用於處理可能產生的不同類型的異常對象。
如果明確知道產生的是何種異常,可以用該異常類作為 catch 的參數;也可以用其父類作為 catch 的參數;
比 如 : 可 以 用 ArithmeticException 類 作 為 參 數 的 地 方 , 就 可 以 用RuntimeException類作為參數, 或者用所有異常的父類Exception類作為參數。但不能是與ArithmeticException類無關的異常, 如NullPointerException( catch中的語句將不會執行) 。
3、捕獲異常的有關信息
與其他對象一樣,可以訪問一個異常對象的成員變量或調用它的方法。
getMessage() 獲取異常信息,返回字符串;
printStackTrace() 獲取異常類名和異常信息,以及異常出現在程序中的位置。返回值void。
4、finally
(1)捕獲異常的最后一步是通過 finally 語句為異常處理提供一個統一的出口,使得在控制流轉到程序的其他部分以前,能夠對程序的狀態作統一的管理;
(2)不論在 try 代碼塊中是否發生了異常事件,catch 語句是否執行,catch 語句是否有異常,catch 語句中是否有 return,finally 塊中的語句都會被執行。
(3)finally 語句和 catch 語句是任選的;
四、捕獲異常:try...catch...finally
1、語法格式
try{ ...... //可能產生異常的代碼
} catch( ExceptionName1 e ){ ...... //當產生ExceptionName1型異常時的處置措施
} catch( ExceptionName2 e ){ ...... //當產生ExceptionName2型異常時的處置措施
} [ finally{ ...... //無論是否發生異常, 都無條件執行的語句
} ]
try:該代碼塊中編寫可能產生異常的代碼
catch:用來進行某種異常的捕獲,實現對捕獲到的異常進行處理。
注意:
(1)try和catch都不能單獨使用,必須連用。
(2)try中可能會拋出多個異常對象,那么就可以使用多個catch來處理這些異常對象
(3)如果try中產生了異常,那么就會執行 catch 中的異常處理邏輯,執行完畢 catch 中的處理邏輯,繼續執行try...catch之后的代碼;
如果 catch 無法捕獲 try 中發生的異常,那么就會導致當前方法結束,並把異常對象拋出給調用者,如果調用者可以處理,那么從調用者處理代碼的后面繼續運行,否則繼續向上拋出,最終達到 JVM;
如果try中沒有產生異常,那么就不會執行catch中異常的處理邏輯,執行完try中的代碼,繼續執行try...catch之后的代碼
2、多個 catch 分析,如何匹配和執行的?
從上到下依次判斷,一旦有一個滿足了,后面就不看了。
建議:如果多個catch中的異常類型有大小包含關系,那么小的在上,大的在下,如果沒有大小包含關系,順序隨意。
Demo:
1 public class TryCatchDemo { 2 public static void main(String[] args) { 3 try {// 當產生異常時,必須有處理方式。要么捕獲,要么聲明。
4 read("b.txt"); 5 } catch (FileNotFoundException e) {// 括號中需要定義什么呢?
6 //try中拋出的是什么異常,在括號中就定義什么異常類型
7 System.out.println(e); 8 } 9 System.out.println("over"); 10 } 11 /* 12 當前的這個方法中 有異常 有編譯期異常 13 */
14 public static void read(String path) throws FileNotFoundException { 15 if (!path.equals("a.txt")) {//如果不是 a.txt這個文件
16 // 我假設 如果不是 a.txt 認為 該文件不存在 是一個錯誤 也就是異常 throw
17 throw new FileNotFoundException("文件不存在"); 18 } 19 } 20 }
擴展:Throwable類中定義了一些獲取異常信息的方法(異常對象常用的方法)
public String getMessage() :獲取異常的簡短描述信息,原因(提示給用戶的時候,就提示錯誤原因)
public String toString() :獲取異常的類型和異常詳細描述信息(不用)。
public void printStackTrace() :打印異常的跟蹤棧信息並輸出到控制台,JVM打印異常對象,默認此方法,打印的異常信息是最全面的
Tips:包含了異常的類型,異常的原因,還包括異常出現的位置,在開發和調試階段,都得使用printStackTrace
Demo:
System.out.println(e.getMessage());
System.out.println(e.toString());
System.out.println(e);
3、finally 代碼塊
finally:有一些特定的代碼無論異常是否發生,都需要執行。另外,因為異常會引發程序跳轉,導致有些語句執行不到。而finally就是解決這個問題的,在finally代碼塊中存放的代碼都是一定會被執行的。
必須執行的代碼:在try語句塊中打開了一些物理資源(磁盤文件/網絡連接/數據庫連接等),都得在使用完之后,最終關閉打開的資源或斷開連接等操作。
語法格式:
try{
可能產生異常的代碼
}catch(定義一個異常的變量,用來接收try中拋出的異常對象){
異常的處理邏輯,異常異常對象之后,怎么處理異常對象
//記錄日志/打印異常信息/繼續拋出異常
}
...
catch(異常類名 變量名){
}finally{
無論是否出現異常都會執行
}
注意:
(1)finally不能單獨使用,必須和try一起使用
(2)finally一般用於資源釋放(資源回收),無論程序是否出現異常,最后都要資源釋放(IO)
Demo:
1 public static void main(String[] args) { 2 try { 3 //可能會產生異常的代碼
4 readFile("c:\\a.tx"); 5 } catch (IOException e) { 6 //異常的處理邏輯
7 e.printStackTrace(); 8 } finally { 9 //無論是否出現異常,都會執行
10 System.out.println("資源釋放"); 11 } 12 } 13
14 /* 15 如果傳遞的路徑,不是.txt結尾 16 那么就拋出IO異常對象,告知方法的調用者,文件的后綴名不對 17 18 */
19 public static void readFile(String fileName) throws IOException { 20
21 if(!fileName.endsWith(".txt")){ 22 throw new IOException("文件的后綴名不對"); 23 } 24
25 System.out.println("路徑沒有問題,讀取文件"); 26 }
Tips:當只有在 try 或者 catch 中調用退出 JVM 的相關方法(System.exit(0);),此時finally才不會執行,否則finally永遠會執行。
4、
五、finally 與 return 混用
(1)不管try中是否發生異常,也不管catch是否可以捕獲異常,也無論try或catch中是否有return。 finally中的代碼都必須執行;
(2)如果finally中有return,就從finally塊的的return回去。
(3)如果finally中沒有return,那么先把try或catch中該執行的執行完(包括把返回值的結果放到要帶回調用處的操作數棧的位置),在return結束當前方法之前,先走一下finally,然后回去結束當前方法;
結論:如果 finally 中沒有 return,finally 中的代碼不影響返回值;
Demo1:
1 public static void main(String[] args) { 2 String str = getNum(1); 3 System.out.println(str); 4 } 5
6 public static String getNum(int a){ 7 try{ 8 System.out.println(a/0); 9 if(a > 0){ 10 return "正數"; 11 }else if(a < 0){ 12 return "負數"; 13 }else{ 14 return "零"; 15 } 16 }catch(Exception e){ 17 return "異常"; 18 }finally{ 19 return "最終"; 20 } 21 }
運行結果:
最終
Demo2:
1 public static void main(String[] args) { 2 String str = getNum(1); 3 System.out.println(str); 4 } 5
6 public static String getNum(int a){ 7 try{ 8 System.out.println(a/0); 9 if(a > 0){ 10 return "正數"; 11 }else if(a < 0){ 12 return "負數"; 13 }else{ 14 return "零"; 15 } 16 }catch(Exception e){ 17 System.out.println("exception"); 18 return "異常"; 19 }finally{ 20 System.out.println("finally"); 21 } 22 }
運行結果:
exception
finally
異常
Demo3:
1 public static void main(String[] args) { 2 int num = getNum(4); 3 System.out.println(num);//0
4 } 5
6 public static int getNum(int a){ 7 int result = 10; 8 try{ 9 System.out.println(a/0); 10 if(a > 0){ 11 result = 20; 12 return result; 13 }else if(a < 0){ 14 result = -20; 15 return result; 16 }else{ 17 return result; 18 } 19 }catch(Exception e){ 20 System.out.println("exception"); 21 result = 0; 22 return result; //從這里返回,result=0
23 }finally{ 24 result = 30; 25 System.out.println("finally"); 26 // return result;//如果有這句,結果就變成30
27 } 28 }
運行結果:
exception
finally
0
Demo4:
1 public class Test { 2 { 3 System.out.println("b"); 4 } 5 static{ 6 System.out.println("a"); 7 } 8 TestExer4(){ 9 System.out.println("c"); 10 } 11 public static String getOut(){ 12 try{ 13 return "1"; 14 }catch(Exception e){ 15 return "2"; 16 }finally{ 17 return "3"; 18 } 19 } 20 public static void main(String[] args) { 21 System.out.println(getOut());//3
22 } 23 }
運行結果:
a
3