第8章 Java異常處理
主要内容
8.1 異常處理的基礎知識
即使是有經驗的程式員,也難免出現程式設計錯誤。
程式設計錯誤的分類
8.1 異常處理的基礎知識
運作時錯誤(runtime error)
在程式運作時出現的一些非正常的現象被稱為運作時錯誤,如除數為0、數組下标越界、檔案不存在、記憶體不夠用等等。
分類:根據錯誤性質将運作時錯誤分為兩類
緻命性的錯誤
非緻命性的異常
8.1 異常處理的基礎知識
異常處理的類層次
Java中預定義了很多異常類,每個異常類代表一種運作錯誤。
8.1 異常處理的基礎知識
常用Exception類的子類:
8.1 異常處理的基礎知識
未被捕獲“異常”
“異常”對象是Java運作時對某些“異常”情況作出反應而産生的。如果不處理“異常”會有什麼樣的情況發生?
例8.1 被0除異常。
class TestException1{
public static void main(String args[]){
int d=0;
int a=42/d;
}
}
8.1 異常處理的基礎知識
程式的輸出結果如圖所示:
8.1 異常處理的基礎知識
class TestException2{
static void subRoutine(){
int d=0;
int a=10/d;
}
public static void main(String args[]){
TestException2.subRoutine();
}
}
8.2 異常處理機制
Java提供了異常處理機制,通過面向對象的方法來處理異常。
在程式運作的過程中,如果發生了異常,則該程式(或Java虛拟機)生成一個代表該異常類的對象(包含該異常的詳細資訊),并把交給運作時系統;
運作時系統從生成對象的代碼開始,沿方法的調用棧逐層回溯查找,直到找到包含相應處理代碼的方法,并把異常對象交給該方法,來處理這一異常。
8.2 異常處理機制
使用try-catch-finally語句捕獲和處理異常
格式:try{
//産生異常的語句
}catch(異常類1 變量){
//異常處理代碼
}catch(異常類2 變量){
//異常處理代碼
}[finally{
}]
8.2 異常處理機制
try語句塊
将程式中可能出現異常的代碼放入try塊中。
當try塊中有語句引發異常時,系統将不再執行try塊中未執行的語句,而執行比對的catch塊。
如果try塊中沒有語句引發異常,則程式執行完try塊中的語句後不執行catch塊中的語句,即跳過catch語句,繼續執行後面的程式。
catch塊
每個try語句後面必須伴随一個或多個catch語句,用于捕捉try語句塊所産生的異常并作相應的處理。
8.2 異常處理機制
catch子句的目标是解決“異常”情況,并像沒有出錯一樣繼續運作。
例如:try{
int d=0;
int a=42/d;
}catch(ArithmeticException e){
System.out.println(e.toString());
}
8.2 異常處理機制
例8.3 try-catch語句的執行順序。
int a = 0,b = 0,c = 0; Random r=new Random();
for (int i = 0; i < 5; i++){
try{
b = r.nextInt(); c = r.nextInt();
a = 12345 / ( b / c );
}catch(ArithmeticException e){
a = 0;
System.out.print("有被0除異常 ");
}
System.out.println("a:" + a);
}
8.2 異常處理機制
注意:一個try和它的catch語句組成了一個單元。catch子句的範圍限制于try語句塊中的語句。一個catch語句不能捕獲另一個try聲明所引發的異常(除非是嵌套的try語句情況)。被try保護的語句聲明必須在一個大括号之内。try語句塊不能單獨使用。
8.2 異常處理機制
多個catch塊 一個catch塊隻能處理一類異常,當try塊中的語句組可能抛出多種異常時,就需要有多個catch塊來分别處理各種異常。
try{
int a=args.length; System.out.println("a=" + a);
int b=42/a; int c[]={1}; c[42]=99;
}catch(ArithmeticException e){
System.out.println("div by 0:" + e);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("array index oob:" + e);
}
8.2 異常處理機制
一個異常對象能否被一個catch塊接收主要看該異常對象與catch塊中聲明的異常類的比對情況,當它們滿足下面條件中的任一條時,異常對象将被接受:
異常對象是catch塊中聲明的異常類的執行個體;
異常對象是catch塊中聲明的異常類的子類的執行個體;
異常對象實作了catch塊中聲明的異常類的接口。
當使用多個catch塊時,需注意catch子句排列順序--先特殊到一般,也就是子類在父類前面。如果子類在父類後面,子類将永遠不會到達。
例8.5 catch語句塊順序的示例。
8.2 異常處理機制
try語句的嵌套 一個try語句可以在另一個try塊内部----try語句的嵌套。
每次進入try語句,異常的前後關系都會被壓入堆棧。如果一個内部的try語句不含特殊異常的catch處理程式,堆棧将彈出,下一個try語句的catch處理程式将檢查是否與之比對。
這個過程将繼續直到一個catch語句比對成功,或者是直到所有的嵌套try語句被檢查耗盡。
如果沒有catch語句比對,Java的運作時系統将處理這個異常。
8.2 異常處理機制
例8.6 運用嵌套try語句的示例。
分析:
(1)當在沒有指令行參數的情況下運作程式,外面的try塊将産生一個被零除的異常。
(2)程式在有一個指令行參數條件下運作,内部的try塊将産生一個被零除的異常。因為與内部的catch塊不比對,它将把該異常傳給外部的catch塊來處理。
(3)如果在具有兩個指令行參數的條件下執行該程式,由内部try塊産生一個數組下标越界異常,由内部的catch塊處理。
8.2 異常處理機制
finally語句塊
某些情況下,不管異常是否發生,都需要處理某些語句,那麼就将這些語句放到finally語句塊中。finally語句所包含的代碼在任何情況下都會被執行。
一個try語句至少有一個catch語句或finally語句與之比對,但比對的catch可以有多個,而finally語句隻能有一個,并且finally語句并非必須有的。
例8.7 finally的用法示例。main()方法中的語句為:
String[] friends={"Tom","葫蘆娃","孫悟空"};
8.2 異常處理機制
try{
for(int i=0;i<5;i++){
System.out.println(friends[i]);
}
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("index err");
return;
}finally{
System.out.println("in finally block!");
}
System.out.println("this is the end");
8.2 異常處理機制
抛出異常
Java運作時系統引發的異常
異常的抛出
根據需要人工建立并抛出
人工抛出異常
文法格式:throw 異常類對象;
例如:IOException e = new IOException();
throw e;
8.2 異常處理機制
例8.7 throw語句的使用。
分析:
(1)main()調用方法demoproc()的過程中捕獲并處理空指針異常。
(2)demoproc()方法也進行了相同的異常處理并且捕獲該異常後将該異常抛出。
(3)建立異常對象:throw new NullPointerException(); 或 throw new NullPointerException(“demo”);
8.2 異常處理機制
聲明抛棄異常
如果一個方法中的代碼在運作時可能生成某種異常,但是在本方法中不必要,或者不能确定如何處理此類異常時,則可以使用throws聲明抛棄異常;
表明該方法中将不對此類異常進行處理,而由該方法的調用者負責處理;
即系統将在調用該方法的上層方法體内尋找合适的異常處理代碼,而不再繼續執行該方法的正常處理流程。
8.2 異常處理機制
聲明抛棄異常的格式 類型 方法名([參數表]) throws 異常類型,…{ //方法體; } 例8.8 聲明抛棄異常。 static void procedure() { System.out.println("inside procedure"); throw new IllegalAccessException("demo"); } public static void main(String args[]) { procedure(); }
8.2 異常處理機制
static void procedure() throws IllegalAccessException{
System.out.println("inside procedure");
throw new IllegalAccessException("demo");
}
public static void main(String args[]){
try{
procedure();
}catch(IllegalAccessException e){
System.out.println("caught" + e);
}
}
8.3 自定義異常類
雖然Java的内置異常處理能夠處理大多數常見錯誤,但使用者仍需建立自己的異常類型來處理特殊情況。這時可以通過建立Exception的子類來定義自己的異常類。
格式:class 類名 extends Exception{
… …
}
例8.10 自定義異常類。
分析:Exception類自己沒有定義任何方法。但它繼承了Throwable提供的一些方法。 例如,
public String getMessage(); public void printStackTrace();
8.5 異常處理應注意的問題
在Java中,對異常進行處理需要考慮以下因素:
如果異常事件是在運作時産生的,并且在JDK API中沒有與該異常事件相對應的異常對象,則應建立使用者自定義類型的異常對象。
如果可以預測異常對象的類型并且該異常是在程式運作時發生的,則建議應用JDK API中定義的系統異常類型,并且可以抛出這種類型的異常對象由JVM處理。
如果不能确定異常對象的類型以及異常發生的時機,則應該采用系統類型異常對象并由JVM處理。
8.5 異常處理應注意的問題
對應用程式設計失誤導緻的數組越界、非法變量等類型的異常,如果要全部捕獲所有類型的異常對象,會增加系統開銷,導緻程式的運作效率降低,建議應用程式可以不對此類異常進行捕獲,而交由JVM進行處理。
對于實作輸入/輸出處理、網絡通訊和資料庫通路功能的代碼,必須進行異常對象的捕獲和處理 。
8.6 斷言
從JDK1.4版本開始,Java語言引入了斷言(assert)機制。目的:程式調試
8.6 斷言
如果沒有斷言機制,Java程式通常使用if-else或switch語句進行變量狀态檢查。缺點:
由于檢查的資料類型不完全相同,這樣的語句形式不會統一。
因為檢查僅僅是應用在測試階段,而if-else或switch語句在釋出以後仍然将起作用,如果消除這些代碼就意味着要注釋或者删除這些代碼,如果這些代碼量很大就意味着工作很繁重并存在風險。
使用斷言的優點:
Java程式員用統一的方式處理狀态檢查問題;
斷言隻需在發行的時候關閉該功能即可。
8.6 斷言
斷言的開啟和關閉
在預設情況下斷言是關閉的,是以在使用斷言以前,需要先開啟斷言功能,方法: java –ea MyClass 或者 java –enableassertions MyClass
關閉斷言功能的方法: java –da MyClass 或者 java –disableassertions MyClass
注意:斷言檢查通常在開發和測試時開啟。為了提高性能,在軟體釋出後,斷言檢查通常是關閉的。
8.6 斷言
斷言的使用
Java中使用關鍵字assert标記斷言,文法格式為:
assert Expression1
assert Expression1:Expression2
8.6 斷言
例8-12 斷言的使用。
public class TestAssertion1{
public static void main(String args[]){
int x=10;
System.out.println("Testing Assertion that x==100");
assert x==100:"Our assertion failed!";
System.out.println("Test passed!");
}
}
8.6 斷言
什麼時候使用斷言
通常來說,斷言用于檢查一些關鍵的值,并且這些值對整個程式,或者局部功能的完成有很大的影響。
斷言表達式應該短小、易懂,如果需要評估複雜的表達式,應該使用函數計算。
使用斷言的情況
檢查控制流:在if-else和switch語句中,可以在不應該發生的控制支流上加上assert語句。如果這種情況發生了,assert能夠檢查出來。
8.6 斷言
在私有方法計算前,檢查輸入參數是否有效 對于一些private的方法,要求輸入滿足一些特定的條件,可以在方法開頭使用assert進行參數檢查;對于公共方法,通常不使用斷言檢查
在方法計算後,檢查方法結果是否有效
檢查程式不變量 private boolean isBalance() { …… } // assert isBalance():"balance is destoried";
上機實踐
1.編寫應用程式,從鍵盤輸入10個學生的數學成績,統計及格人數、不及格人數、平均分。要求輸入的數學成績在0~100之間(設計一個自定義異常類NumberRangeException,當輸入的成績不在0~100之間時,抛出該異常類對象,程式中捕獲這個異常并作出相應的處理)。
2.使用者輸入密碼,要求密碼滿足的條件是:長度大于6且包含數字、大寫字母和小寫字母,如果不滿足條件,則抛出UnSafePasswordException自定義異常類對象。