try-catch-finally 異常處理的兩種方式
異常處理
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中改變的是這個地址中的值,所以輸出的值就是改變之後的值。