Java——異常
Java 的異常機制主要依賴於try 、catch 、finally 、throw 和 throws 五個關鍵字,其中 try 關鍵字後緊跟一個花括號括起來的代碼塊(花括號不能省略),簡稱 try 塊,它裏面放置可能引發異常代碼。 catch 後對應異常類型和一個代碼塊,用於表明該 catch 塊用於處理這種類型代碼塊 。 多個 catch 塊後還可以跟一個 finally 塊,finally塊用於回收在 try 塊裏打開的物理資源,異常機制會保證 finally 塊總會被執行。 throws 關鍵字主要在方法簽名中使用,用於聲明該方法可能拋出的異常;而 throw 用於拋出一個實際的異常, throw 可以單獨作為語句使用,拋出一個具體異常對象。
演示程序中異常:
public static void main(String[] args) { Scanner input =new Scanner(System.in); System.out.println("請輸入被除數:"); int num1=input.nextInt(); System.out.println("請輸入被除數:"); int num2= input.nextInt(); System.out.println(String.format("%d /%d =%d", num1,num2,num1/num2)); System.out.println("感謝使用本程序!"); }
在正常情況下,用戶會按照系統的提示輸入整數,除數不為 0 ,運行結果如下:
但是用戶沒按照要求進行輸入,如被除數輸入了“B”,則程序運行時將會發生異常,運行如下:
或者輸入 0,則程序運行時也將發生異常,運行如圖:
從結果我們可以看出一旦出現異常,程序將會立刻結束,不僅計算機和輸出商的語句不被執行,而且輸出“感謝使用本程序!” 的語句也不執行。那麽我們該如何解決異常呢?如果我們用 if ——else語句來對各種異常情況進行來處理。我們會發現代碼加入了大量的異常情況判斷和處理代碼,把很大一部分時間放在了處理異常代碼上,放在了“堵漏洞”上,減少了編寫業務代碼的時間,必然影響開發效率。
那麽同樣一段代碼,把可能出現異常的代碼放入 try 語句塊中,並使用 catch 語句塊捕獲異常。
public static void main(String[] args) { Scanner input =new Scanner(System.in); System.out.println("請輸入被除數:"); int num1=input.nextInt(); try { System.out.println("請輸入被除數:"); int num2= input.nextInt(); System.out.println(String.format("%d /%d =%d", num1,num2,num1/num2)); System.out.println("感謝使用本程序!"); } catch (Exception e) { System.out.println("出現錯誤:被除數和除數必須是整數,"+"除數不能為0"); e.printStackTrace(); } }
再次輸入以上的三種情況,
第一種:
第二種:
第三種:
如果希望無論是否發生異常,都執行輸出“感謝使用蹦本程序!” 的語句,,那麽只需要在 try——catch 語句塊後加 finally 塊,把該語句放入finally 塊,無論發生異常,finally 開中的代碼總能被執行。
public static void main(String[] args) { Scanner input =new Scanner(System.in); System.out.println("請輸入被除數:"); int num1=input.nextInt(); try { System.out.println("請輸入被除數:"); int num2= input.nextInt(); System.out.println(String.format("%d /%d =%d", num1,num2,num1/num2)); } catch (Exception e) { System.out.println("出現錯誤:被除數和除數必須是整數,"+"除數不能為0"); e.printStackTrace(); } finally{ System.out.println("感謝使用本程序!"); } }
結果:
即使在 try 塊和 catch 塊中存在 return 語句,finally 塊中語句也會被執行。
public static void main(String[] args) { Scanner input =new Scanner(System.in); System.out.println("請輸入被除數:"); int num1=input.nextInt(); try { System.out.println("請輸入被除數:"); int num2= input.nextInt(); System.out.println(String.format("%d /%d =%d", num1,num2,num1/num2)); return; //finally語句塊仍舊會被執行 } catch (Exception e) { System.out.println("出現錯誤:被除數和除數必須是整數,"+"除數不能為0"); return; //finally語句塊仍舊會被執行 } finally{ System.out.println("感謝使用本程序!"); } }
執行順序:執行 try 塊或 catch 中之前的語句,執行 finally 塊中的語句,執行 try 塊或 catch 中的 return 語句退出。
運行結果:
finally 塊中語句不被執行的唯一情況:在異常處理代碼中執行System.exit(1),將退出Java虛擬機。
public static void main(String[] args) { Scanner input =new Scanner(System.in); System.out.println("請輸入被除數:"); int num1=input.nextInt(); try { System.out.println("請輸入被除數:"); int num2= input.nextInt(); System.out.println(String.format("%d /%d =%d", num1,num2,num1/num2)); } catch (Exception e) { System.out.println("出現錯誤:被除數和除數必須是整數,"+"除數不能為0"); System.exit(1); } finally{ System.out.println("感謝使用本程序!"); } }
運行結果:
》多重catch塊
一段代碼可能會引發多種類型異常,這時,可以在一個 try 語句塊後面跟多個 catch 語句塊,分別處理不同的異常。但排列順序必須是從子類到父類,最後一個一般都是Execption 類,因為所有異常子類都繼承自 Exception類,所以如果將父類異常放到前面,那麽所有的異常都將被捕獲,後面 catch 塊中的子類異常講的不到被執行的機會。當運行時,系統沖上到下分別對每個 catch 語句塊處理的異常的異常類型進行檢測,並執行第一個與異常類型匹配的 catch語句。執行其中的一條 catch 語句之後,其後的 catch 語句都將被忽略。
public static void main(String[] args) { Scanner input =new Scanner(System.in); System.out.println("請輸入被除數:"); int num1=input.nextInt(); try { System.out.println("請輸入被除數:"); int num2= input.nextInt(); System.out.println(String.format("%d /%d =%d", num1,num2,num1/num2)); } catch (InputMismatchException e) { System.err.println("被除數和除數必須是整數。"); }catch (ArithmeticException e) { System.err.println("除數不能為零。"); } catch (Exception e) { System.err.println("其它未知。"); }finally{ System.out.println("感謝使用本程序!"); } }
運行結果:
》聲明異常——throws
如果一個方法體中拋出異常,我們就希望調用者能及時地捕獲異常,那麽如何通知調用著呢?Java 語言中通過關鍵字 throws 聲明某個方法可能拋出的各種異常,throws 可以拋出多個異常,之間用逗號隔開。
public static void main(String[] args) { try { divide(); } catch (Exception e) { System.err.println("出現錯誤:被除數和除數必須是整數" + "除數不能為零"); } finally { System.out.println("感謝使用本程序!"); } } public static void divide() throws Exception { Scanner input = new Scanner(System.in); System.out.println("請輸入被除數:"); int num1 = input.nextInt(); System.out.println("請輸入被除數:"); int num2 = input.nextInt(); System.out.println(String.format("%d /%d =%d", num1, num2, num1 / num2)); }
從上面看出 divide() 方法使用 throws 聲明拋出了異常,並在 main 方法裏面調用了此方法,通過 try —— catch 捕獲了異常。如果不通過 try ——catch 處理異常,我們可以繼續使用 throws 拋出異常
public static void main(String[] args) throws Exception { divide(); } public static void divide() throws Exception { Scanner input = new Scanner(System.in); System.out.println("請輸入被除數:"); int num1 = input.nextInt(); System.out.println("請輸入被除數:"); int num2 = input.nextInt(); System.out.println(String.format("%d /%d =%d", num1, num2, num1 / num2)); }
但是使用這種方法需要註意:調用者拋出的異常一定要比被調用拋出的異常更大或等於。
》拋出異常——throw
除了系統自動拋出異常,在編程過程中,我們往往遇到這樣的情形:有些問題是系統無法自動發現並解決的,如年齡不在正長範圍內、性別輸入不是 “男” 或 “女” 等,此時需要程序員而不是系統來自動拋出異常,把問題提交給調用者去解決。
》使用 throw 在方法內拋出異常
public class Person { private String sex; public String getSex() { return sex; } public void setSex(String sex) throws Exception { if ("男".equals(sex) || "女".equals(sex)) { this.sex = sex; } else { throw new Exception("性別必須是\"男\"或者\"女\"!"); } } }
public static void main(String[] args) throws Exception { Person person = new Person(); person.setSex("hao"); }
輸出結果:
throw 和 throws 的區別表現在以下三個方面。
》作用不同 : throw 用於在程序中拋出異常;throws 用於聲明在該方法內拋出了異常。
》使用位置不同:throw 位於方法體內部,可以作為單獨語句使用;throws 必須跟在方法參數列表的後面,不能單獨使用。
》內容不同:throw 拋出一個異常對象,而且只能是一個;throws 後面跟異常類,而且可以跟多個以異常類。
》異常的分類
Throwable 類 :所有異常類型都是 Throwable 類的子類,它派生兩個子類,即Error 和 Exception 。
Error類 :表示緊靠程序本身無法恢復的嚴重錯誤,如內存溢出動態鏈接失敗、虛擬機錯誤。應用程序不應該拋出這種類型的對象(一般是由虛擬機拋出)。假如出現這種錯誤,除了盡力使程序安全退出外,在其他方面是無能為力的。所以在進行程序設計時,應該更關註Exception類。
Exception 類:由Java 應用程序拋出和處理的嚴重錯誤,如需文件找不到、網絡連接不通或中斷。算術運算出錯(如被零除)、數組下標越界、裝載了一個不存在的類、對 null 對象的操作、類型轉換異常類等。他的各種不同的子類分別對應不同類型的異常。
運行時異常:包括RuntimeException 及其所有子類,不要求程序必須對它們做出處理。
Checked 異常(非運行時異常):除了運行時異常外的其他由Exception 繼承來的異常類。程序必須捕獲或者聲明拋出這種異常,否則會出現編譯錯誤,無法通過編譯。處理方式包括來鐘:通過 try——catch 在當前位置捕獲並處理異常;通過throws 聲明拋出異常,交給上一級調用方法處理。
》自定義異常
當 JDK 中的異常類型不能滿足出現的需要時,可以自定義異常類。
public class GenderException extends Exception { public GenderException() { super(); } public GenderException(String message, Throwable cause) { super(message, cause); } public GenderException(String message) { super(message); } public GenderException(Throwable cause) { super(cause); } }
public class Person { private String sex; public String getSex() { return sex; } public void setSex(String sex) throws Exception { if ("男".equals(sex) || "女".equals(sex)) { this.sex = sex; } else { throw new GenderException("性別必須是\"男\"或者\"女\"!"); } } }
Java——異常