異常處理
1. 明確什么是異常 (重點)
2. 能辨識出常見的異常及其含義。 (熟悉+)
3. 理解異常產生的原理 (了解)
4. 能處理異常 (重點)
5. 能夠自定義異常類型 (熟悉)
目的
我們發現異常,捕獲異常的目的是要對異常進行補救,而不是打印一下。
什么是異常?
異常是在程序中導致程序中斷運行的一種指令流。
例如,現在有如下的操作代碼:
public class ExceptionDemo01{ public static void main(String argsp[]){ int i = 10 ; int j = 0 ; System.out.println("============= 計算開始 =============") ; int temp = i / j ; // 進行除法運算 System.out.println("temp = " + temp) ; System.out.println("============= 計算結束 =============") ; } }; 運行結果: ============= 計算開始 ============= Exception in thread "main" java.lang.ArithmeticException: / by zero at ExceptionDemo01.main(ExceptionDemo01.java:6)
以上的代碼在“int temp = i / j ;”位置處產生了異常,一旦產生異常之后,異常之后的語句將不再執行了,所以現 在的程序並沒有正確的執行完畢之后就退出了。
那么,為了保證程序出現異常之后仍然可以正確的執行完畢,所以要采用異常的處理機制。
處理異常
異常處理一般有兩種方式,1.使用catch捕獲異常; 2.使用throws關鍵字在方法聲明時拋出異常
1.catch捕獲異常
如果要想對異常進行處理,則必須采用標准的處理格式,處理格式語法如下:
try{
// 有可能發生異常的代碼段
}catch(異常類型1 對象名1){
// 異常的處理操作 異常補救
}catch(異常類型2 對象名2){
// 異常的處理操作 異常補救
} ...
finally{
// 異常的統一出口
System.out.println("這里的代碼無論是否出現異常都會執行,一般用於資源回收");
}
在進行異常的處理之后,在異常的處理格式中還有一個finally語句,那么此語句將作為異常的統一出口,不管是否產生 了異常,最終都要執行此段代碼。
無論異常是否發生,finally必然執行。讀取文件,占用文件,讀取數據庫等,都在finally中釋放,因為finally必然執行。一般用於資源回收。
try+catch的處理流程
1、 一旦產生異常,則系統會自動產生一個異常類的實例化對象。
2、 那么,此時如果異常發生在try語句,則會自動找到匹配的catch語句執行,如果沒有在try語句中,則會將異 常拋出.
3、 所有的catch根據方法的參數匹配異常類的實例化對象,如果匹配成功,則表示由此catch進行處理。
2.throws throw 拋出異常
在程序中異常的基本處理已經掌握了,但是隨異常一起的還有一個稱為throws關鍵字,此關鍵字主要在方法的聲明上使 用,表示方法中不處理異常,而交給調用處處理。
1.throws是函數方法拋出異常,一般寫在方法的頭部,用來拋出一些異常,本身不進行解決,拋給方法的調用者進行解決(try catch)
格式: 返回值 方法名稱()throws Exception{ }
Integer類: public static int parseInt(String text)throws NumberFormatException
2.throw是語句拋出異常,出現於函數內部,用來拋出一個具體異常實例,throw被執行后面的語句不起作用,直接轉入異常處理階段
一般是用戶自定義的RuntimeException運行時異常,然后使用throw拋出。
1 public class Demo9 { 2 public static void main(String[] args) { 3 } 4 5 /** 6 * 異常是否拋出去, 應該站在哪個角度思考? 7 * 8 * 如果是因為傳參導致異常 , 應該通過throws將異常拋出去. 9 * 10 * @param text 11 * @throws IOException : 因為傳遞的指令不對, 會導致此問題發生(s2=0時) 12 */ 13 public static void shutdown(String text) throws IOException { 14 Runtime.getRuntime().exec(text); 15 } 16 /** 17 * 此方法用於求兩個參數的和 18 * 會將兩個參數 轉換為數字 求和 19 * @param s1 字符串參數1 20 * @param s2 字符串參數2 21 */ 22 public static void sum(String s1,String s2){ 23 int sum = Integer.parseInt(s1)+Integer.parseInt(s2); 24 System.out.println("和是:"+sum); 25 } 26 }
1 public class Person { 2 private String name; 3 private int age; 4 5 public String getName() { 6 return name; 7 } 8 9 public void setName(String name) { 10 this.name = name; 11 } 12 13 public int getAge() { 14 return age; 15 } 16 17 public void setAge(int age) { 18 if(age<0 || age>180){ 19 RuntimeException e = new RuntimeException("年齡不合理"); 20 throw e; 21 }else{ 22 this.age = age; 23 } 24 25 } 26 }
異常體系結構
異常指的是Exception , Exception類, 在Java中存在一個父類Throwable(可能的拋出)
Throwable存在兩個子類:
1.Error:表示的是錯誤,是JVM發出的錯誤操作,只能盡量避免,無法用代碼處理。
2.Exception:一般表示所有程序中的錯誤,所以一般在程序中將進行try…catch的處理。
RuntimeExcepion與Exception的區別
RuntimeException是Exception的子類,
catch(Exception e) 就是范圍最大的捕獲異常。如果為了方便,則可以將所有的異常都使用Exception進行捕獲。
所有RuntimeException的子類即為非檢查型異常;Exception的其余子類都為檢查型異常。所謂“檢查型異常”是指在源代碼例必須顯式地進行捕獲處理,eclipse和idea都是自動編譯的,當我們把代碼寫完的時候,如果有紅線提示或者錯誤提示,那么就是檢查異常。也就是說,當你看到某個方法聲明中可能拋出某個檢查型異常,那么作為調用方必須考慮如何處理這個異常,否則編譯器就是給出錯誤提示。
所謂“非檢查型異常”,也就是運行時異常,編譯的時候不會給錯誤提示,運行的時候有可能出現異常。如: 用戶輸入0作為除數,就會 出現異常。通常是可以通過編碼加以避免的邏輯錯誤,具體根據需要來判斷是否需要捕獲,並不會在編譯期強制要求。例如NullPointerException、ArrayIndexOutOfBoundsException等。也就是說,程序員應該通過合理編碼來努力避免程序出現這類異常,或者說程序出現這類異常就是程序員的責任。
異常處理常見面試題
1. try-catch-finally 中哪個部分可以省略?
答: catch和finally可以省略其中一個 , catch和finally不能同時省略 注意:格式上允許省略catch塊, 但是發生異常時就不會捕獲異常了,我們在開發中也不會這樣去寫代碼.
2. try-catch-finally 中,如果 catch 中 return 了,finally 還會執行嗎?
答:finally中的代碼會執行 詳解:
執行流程:
1. 先計算返回值, 並將返回值存儲起來, 等待返回
2. 執行finally代碼塊
3. 將之前存儲的返回值, 返回出去;
需注意:
1. 返回值是在finally運算之前就確定了,並且緩存了,不管finally對該值做任何的改變,返回的值都不 會改變
2. finally代碼中不建議包含return,因為程序會在上述的流程中提前退出,也就是說返回的值不是try或 catch中的值
3. 如果在try或catch中停止了JVM,則finally不會執行.例如停電- -, 或通過如下代碼退出 JVM:System.exit(0)。
try中有return時,finally也會執行,這個return值在finally執行之前的try中已經准備好了,復制備份放入了緩存。如果return返回的是基本數據類型,如int,那么finally中再給int賦值也不會改變return返回的值。如果return返回的是一個對象,那么復制到緩存中的是這個對象的地址,這時在finally中改變這個對象的值時,返回的值就是改變以后的值。
1 public class Demo7 { 2 public static void main(String[] args) { 3 int a = haha(); 4 System.out.println(a); 5 } 6 public static int haha(){ 7 int a = 10; 8 try{ 9 return a; 10 }catch(Exception e){ 11 12 }finally { 13 a = 20; 14 } 15 return 0; 16 } 17 static class Person{ 18 int age; 19 } 20 }
以上輸出結果是:10
因為finally執行之前,retrun 的a是10,這個值已經復制到緩存中,輸出時,就i時輸出緩存中的值。finally再改變a的值也不會被輸出。
1 public class Demo6 { 2 public static void main(String[] args) { 3 Person p = haha(); 4 System.out.println(p.age); 5 } 6 public static Person haha(){ 7 Person p = new Person(); 8 try{ 9 p.age = 18; 10 return p; 11 }catch(Exception e){ 12 return null; 13 }finally { 14 p.age = 28; 15 } 16 } 17 static class Person{ 18 int age; 19 } 20 }
以上輸出結果是:28
try中return的p對象,放到輸出緩存中是P對象的地址。finally中改變的是這個地址中的值,所以輸出的值就是改變之后的值。