- Java語言程序設計
- 陸遲編著
- 3917字
- 2019-01-09 14:01:53
3.6 異常處理
Java是一個講究安全性的語言。任何可能在程序運行過程中產生打斷程序正常執行的事件都有用來保護的陷阱。Java異常處理機制提供了去檢查及處理產生各種錯誤、異常事件的方法。
3.6.1 異常概述
1.異常的概念
用Java的術語來說,在程序運行過程中發生的、會打斷程序正常執行的事件稱為異常(Exception),也稱為例外。程序運行過程中可能會有許多意料之外的事情發生,例如,零用做了除數(產生算術異常ArithmeticException)、在指定的磁盤上沒有要打開的文件(產生文件未找到異常FileNotFoundException)、數組下標越界(產生數組下標越界異常ArrayⅠndexOutOfBoundsException)等。對于一個實用的程序來說,處理異常的能力是一個不可缺少的部分,它的目的是保證程序在出現任何異常的情況下仍能按照計劃執行。
下面先來看一個Java系統對異常的處理的例子。
【例3.25】Java系統對異常的處理。
public class SystemException{ public static void main(String args[]){ int a = 68; int b = 0; System.out.println(a / b); // 0用做了除數 } }
程序運行結果為:
Exception in thread "main" java.lang.ArithmeticException: / by zero at SystemException.main(SystemException.java:5)
屏幕顯示的信息指明了異常的類型:ArithmeticException: / by zero(算術異常/用0除)。在這個程序中,未進行程序的異常處理。這是因為除數為零是算術異常,它屬于運行時異常(RunTimeException),通常運行時異常在程序中不做處理,Java運行時系統能對它們進行處理。Java語言本身提供的Exception類已考慮了多種異常的處理。
2.Java對異常的處理機制
Java異常處理機制提供了一種統一和相對簡單的方法來檢查及處理可能的錯誤。例如,在程序中出現了除以零的錯誤時,即拋出(throw)一個ArithmeticException異常實例,異常處理程序捕獲(catch)這個異常實例,并對它進行處理。如果系統找不到相應的異常處理程序,則由Java默認的異常處理程序來處理,即在輸出設備上輸出異常信息,同時程序停止運行。有時也可以在程序中寫一些自己的例程來處理異常;也可以將錯誤交給Java系統去處理,從而可以使得程序安全地退出。
3.異常類的層次和主要子類
Java用面向對象的方法處理異常,所有的異常都是Throwable類的子類生成的對象。所有的異常類都是Throwable類的后代。Throwable類的父類是Java的基類(Object),它有兩個直接子類:Error類和Exception類。運行時異常RuntimeException類是Exception類的子類。只有Throwable類的后代才可以作為一個異常被拋出。Java語言的異常處理的主要子類見表3.1~表3.3。
4.異常類的方法和屬性
(1)異常類的構造方法
構造方法是一類方法名與類名相同的方法,用于創建并初始化該類的對象。Exception類有四個重載的構造方法,常用的兩個構造方法為:
· public Exception()創建新異常。
· public Exception(String message); 用字符串參數message描述異常信息創建新異常。
(2)異常類的方法
Exception類常用的方法有:
· public String toString()返回描述當前異常對象信息的字符串。
· public String getMessage()返回描述當前異常對象的詳細信息。
· public void printStackTrace()在屏幕上輸出當前異常對象使用堆棧的軌跡,即程序中先后調用了哪些方法,使得運行過程中產生了這個異常對象。
【例3.26】運行時異常產生時的信息顯示。
class ExceptionDemo{ public static void main(String[] args) { String s = "123.45"; methodA(s); } static void methodA(String s) {
Integer i = new Integer(s); System.out.println(i); } }
程序運行結果如下:
Exception in thread "main" java.lang.NumberFormatException: 123.45 at java.lang.Integer.parseInt(Integer.java:438) at java.lang.Integer.<init>(Integer.java:570) at MyClass.methodA(MyClass.java:7) at MyClass.main(MyClass.java:4)
本程序運行結果說明程序運行時產生一個NumberFormatException數值格式異常。在用構造方法Ⅰnteger將一個字符串轉換為Ⅰnteger數據時,參數字符串格式不對,所以產生了這個運行時異常,Java系統(即系統調用方法printStackTrace())將調用堆棧的軌跡打印了出來。輸出的第一行信息也是toString()方法輸出的結果,對這個異常對象進行簡單說明。其余各行顯示的信息表示了異常產生過程中調用的方法,最終是在調用 Ⅰnteger.parseⅠnt()方法時產生的異常,調用的出發點在main()方法中。
3.6.2 異常處理
在Java語言中,異常有幾種處理方式:
(1)可以不處理運行時異常,由Java虛擬機自動進行處理。
(2)使用try-catch-finally語句捕獲異常。
(3)通過throws子句聲明拋棄異常,還可以自定義異常,用throw語句來拋出它。
1.運行時異常
運行時異常是Java運行時系統在程序運行中檢測到的,可能在程序的任何部分發生,而且數量可能較多,如果逐個處理,工作量很大,有可能影響程序的可讀性及執行效率,因此,Java編譯器允許程序不對運行時異常進行處理,而將它交給默認的異常處理程序,一般的處理方法是在屏幕上輸出異常的內容以及異常的發生位置。如例3.25 和例3.26 所示。當然,在必要的時候,也可以聲明、拋出、捕獲運行時的異常。
表3.1 Error類

表3.2 Exception類

表3.3 RuntimeException類

2.try-catch-finally語句
在Java語言中,允許自己來處理異常。Java語言提供try-catch-finally語句來捕獲和處理異常,該語句的格式如下:
try{ 語句 // 可能產生異常的語句 }catch(Throwable-subclass e){ // 異常參數 語句 // 異常處理程序 }catch(Throwable-subclass e){ // 異常參數 語句 // 異常處理程序 }…
finally{ 語句 }
try語句塊中是可能產生異常對象的語句,一旦其中的語句產生了異常,程序即跳到緊跟其后的第一個catch子句。try程序塊之后的catch子句可以有多個,也可以沒有。
catch子句中都有一個代表異常類型的形式參數,這個參數指明了這個catch程序塊可以捕獲并處理的異常類型。若產生的異常類型與catch子句中聲明的異常參數類型匹配,則執行這個catch程序塊中的異常處理程序。匹配的意思是指異常參數類型與實際產生的異常類型一致或是其父類。若不匹配則順序尋找下一個catch子句,因此catch語句的排列順序應該從特殊到一般,否則,放在后面的catch語句將永遠執行不到。
也可以用一個catch語句處理多個異常類型,這時它的異常類型參數應該是這多個異常類型的父類。
若所有catch參數類型與實際產生的異常類型都不匹配,則標準異常處理程序將被調用,即在輸出設備上輸出異常信息,同時程序停止運行。
【例3.27】捕獲除數為零的異常,并顯示相應信息。
class ExceptionDemo1{ public static void main(String args[]) { int d, a; try { // 監控可能產生異常的代碼塊 d = 0; a = 68 / d; System.out.println("本字符串將不顯示。"); } catch (ArithmeticException e) { // 捕獲divide-by-zero錯誤 System.out.println("產生用零除錯誤。"); } System.out.println("在捕獲語句后。"); } }
程序運行結果如下:
產生用零除錯誤。 在捕獲語句后。
【例3.28】多個catch子句的try語句。
class ExceptionDemo2 { public static void main(String args[]) { try { int a = args.length; System.out.println("a = " + a); int b = 42 / a; int c[] = { 1 }; c[4] = 99; }catch(ArithmeticException e) { // 捕獲算術運算異常 System.out.println("Divide by 0: " + e); }catch(ArrayIndexOutOfBoundsException e) { // 捕獲數組下標越界異常 System.out.println("Array index oob: " + e);
} System.out.println("After try/catch blocks."); } }
程序運行結果如下:
a = 0 Divide by 0: java.lang.ArithmeticException: / by zero After try/catch blocks.
catch子句中異常參數的聲明原則是從特殊到一般,若將一般(范圍寬)的異常參數放到了前面,特殊(范圍窄)的異常參數放到了后面,編譯系統會指出下列錯誤:
… : catch not reached.
這是提示后面的catch子句根本不會被執行到,因為它能捕獲的異常已經被前面的catch子句捕獲了。
try語句中的finally子句的作用是說明必須執行的語句,無論try程序塊中是否拋出異常,finally程序塊中的語句都會被執行到。
【例3.29】有finally子句的try語句。
class ExceptionDemo3{ public static void main(String args[]){ try{ int x=0; int y=20; int z=y/x; System.out.println("y/x的值是: "+z); }catch(ArithmeticException e){ System.out.println("捕獲到算術異常:" + e); }finally{ System.out.println("執行到finally塊內!"); try{ String name = null; if(name.equals("張三")){ // 字符串比較,判斷name是否為"張三" System.out.println("我的名字叫張三"); } }catch(Exception e){ System.out.println("又捕獲到異常:" + e); }finally{ System.out.println("執行到內層finally塊內!"); } } } }
程序運行結果如下:
捕獲到算術異常:java.lang.ArithmeticException:/ by zero 執行到finally塊內! 又捕獲到異常:java.lang.NullPointerException
執行到內層finally塊內!
在Java語言中,try-catch-finally語句允許嵌套。本例中是將內層的try嵌套在外層的finally塊內。在程序執行到外層的try程序塊時,由于分母為零而產生了算術異常,所以程序轉移到第一個catch塊。該catch捕獲了這個算術異常,并進行了處理,之后程序轉向必須執行的外層的finally程序塊。因為該finally塊產生空指針異常(一個null字符串和字符串“張三”進行比較),所以內層catch子句再次捕獲到異常,最后程序轉移到內層的finally程序塊。
finally塊還可以和break、continue和return等流程控制語句一起使用。當try程序塊中出現了上述這些語句時,程序必須先執行finally程序塊,才能最終離開try程序塊。
【例3.30】break與finally的聯用。
class BreakAndFinally{ public static void main(String args[]){ for(;;) try{ System.out.println("即將退出循環了!"); break; }finally{ System.out.println("finally塊總要被執行到!"); } } }
程序運行結果如下:
即將退出循環了! finally塊總要被執行到!
3.throw語句和throws子句
throw語句可使用戶自己根據需要拋出異常。throw語句以一個異常類的實例對象作為參數。一般的形式是:
方法名(arguments)throws異常類{ ... throw new異常類(); }
new運算符被用來生成一個異常類的實例對象。例如:
throw new ArithmeticException();
包含throw語句的方法要在方法頭參數表后書寫throws子句。它的作用是通知所有要調用此方法的其他方法,可能要處理該方法拋棄的那些異常。若方法中的throw語句不止一個,throws子句應指明拋棄的所有可能產生的異常。
通常使用throws子句的方法本身不處理本方法中產生的異常,聲明拋棄異常使得異常對象可以從調用棧向后傳播,直到有合適的方法捕獲它為止。對未用throw語句產生系統異常的方法,也可以使用throws子句來聲明拋棄異常。例2.9 和例2.10 就是這種情況,在main()方法中不對異常進行處理。
為了能捕獲throw拋出的異常,應在try塊中調用包含throw語句的方法。
throw語句和throws子句的使用見下例。
【例3.31】throw語句和throws子句的使用。
class ThrowDemo{ void inException()throws ArithmeticException{ throw new ArithmeticException(); } public static void main(String args[]){ ThrowDemo s = new ThrowDemo(); try{ s.inException(); }catch(Exception e){ System.out.println("異常來了:" + e); } } }
程序運行結果如下:
異常來了: java.lang.ArithmeticException
程序中通過throw語句拋出一個算術異常,包含throw語句的inException ()方法頭中加入了throws子句,它指明要拋棄算術異常。inException ()方法的調用發生在try塊中以捕獲拋出的異常,捕獲異常的情況與前面見過的用零除情況一樣。
需注意的是:throw語句一般應放入分支語句中,表示僅在滿足一定條件后才被執行,而且throw語句后不允許有其他語句,否則將出現編譯錯誤信息:unreachable statement。
4.創建自己的異常
在例3.31 中,用throw拋出的異常類是系統提供的算術異常類。Java語言還允許定義用戶自己的異常類,從而實現用戶自己的異常處理機制。使用異常處理使得自己的程序足夠健壯以從錯誤中恢復。定義自己的異常類要繼承Throwable類或它的子類,通常是繼承Exception類。
【例3.32】設計自己的異常。從鍵盤輸入一個double類型的數,若不小于0.0,則輸出它的平方根,若小于0.0,則輸出提示信息“輸入錯誤!”。
import java.io.*; import javax.swing.JOptionPane; class MyException extends Exception{ void test(double x)throws MyException{ if(x < 0.0)throw new MyException(); // 條件成立時,執行throw語句 else System.out.println(Math.sqrt(x)); } public static void main(String args[])throws IOException{ MyException n = new MyException(); try{ String s = JOptionPane.showInputDialog("請輸入一個實數:"); n.test(Double.parseDouble(s)); }catch(MyException e){ System.out.println("輸入錯誤!"); }
} }
程序的兩次運行結果如下:
(1)求輸入實數的平方根。請輸入一個實數:123.5
11.113055385446435
(2)求輸入實數的平方根。請輸入一個實數:-789
輸入錯誤!
在本程序中,自己的異常類通過extends繼承了Exception異常類。在test()方法中,用throw語句指定了可能拋出的異常,這個語句在調用test()方法時,參數小于0時被執行,產生異常并拋出。
- Visual FoxPro程序設計教程
- 軟件測試項目實戰之性能測試篇
- Learning Apache Mahout Classification
- 量化金融R語言高級教程
- Python忍者秘籍
- Apache Spark 2.x for Java Developers
- Nginx Lua開發實戰
- 深入分布式緩存:從原理到實踐
- 零基礎學Python編程(少兒趣味版)
- Kotlin Programming By Example
- 創意UI Photoshop玩轉移動UI設計
- C/C++代碼調試的藝術(第2版)
- R語言:邁向大數據之路
- 前端Serverless:面向全棧的無服務器架構實戰
- Splunk Developer's Guide(Second Edition)