天天看點

第8章 Java異常處理

第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自定義異常類對象。