java編程思想讀書筆記 第十二章 通過異常處理錯誤(上)

java異常處理的目的在於通過使用少於目前數量的代碼來簡化大型、可靠的程序的生成,並且通過這種方式可以使你更加自信。本章將介紹如何編寫正確的異常處理程序,並將展示當方法出問題的時候,如何產生自定義異常。

1.概念
如果不使用異常,那麼就必須檢查特定的錯誤,並在程序中的許多地方處理它。而如果使用異常,那就不必在方法調用處進行檢查,因爲異常機制將保證能夠捕獲這個錯誤。並且,只需在一個地方處理錯誤,即所謂的異常處理程序中。這種方式不僅節省代碼,而且把“描述在正常執行過程中做什麼事”的代碼和“出了問題怎麼辦”的代碼相分離。總之,與以前的錯誤處理方法相比,異常機制使代碼的閱讀、編寫和調試工作更加井井有條。
2.基本異常
異常情形是指組織當前方法或作用域繼續執行的問題。把異常情形與普通問題相區分很重要,所謂的普通問題是指,在當前環境下能夠得到足夠的信息,總能處理這個錯誤。而對於異常情形,就不能繼續下去了,因爲在當前環境下無法獲得必要的信息來解決問題。你所能做的就是從當前環境跳出,並且把問題交給上一級環境。這就是拋出異常時所發生的事情。
當拋出異常後有幾件事會發生。首先,同java中其他對象的創建一樣,將使用new在堆上創建異常對象。然後,當前的執行路徑被終止,並且從當前環境中彈出對異常對象的引用。此時,異常處理機制接管程序,並開始尋找一個恰當的地方來執行程序。這個恰當的地方就是處理異常程序,它的任務是將程序從錯誤狀態中恢復,以使程序能要麼換一種方式運行,要麼繼續執行下去。
異常允許我們強制程序停止運行,並告訴我們出現了什麼問題,或者強制程序處理問題,並返回到穩定狀態。
2.1異常參數
與使用java的其他對象一樣,我們總是用new在堆上創建異常對象,這也伴隨着存儲空間的分配和構造器的調用。所有標準異常類都有兩個構造器:一個是默認構造器,另一個是接受字符串作爲參數,以便能把相關的信息放入異常對象的構造器:
throw new NullPointerException(“T = null”);
在使用new創建了異常對象之後,此對象的引用將傳給throw。儘管返回的異常對象類型通常與方法設計的返回類型不同,但從效果上看,它就像是從方法“返回”的。可以簡單地把異常處理看成是一種不同的返回機制,當然若過分調用這種類比的話,就會有麻煩了。另外還能用拋出異常的方式從當前的作用域退出。在這種情況下,將會返回一個異常對象,然後退出方法或作用域。
此外,能夠拋出任意類型的Throwable對象,它是異常類型的根類。通常,對於不同類型的錯誤,要拋出相應的異常。錯誤信息可以保存在異常對象內部或者用異常類的名稱來暗示。
3.捕獲異常
要明白異常時如何被捕獲的,必須先理解監控區域的概念。它是一段可能產生異常的代碼,並且後面跟着處理這些異常的代碼。
3.1 try塊
如果在方法內部拋出了異常,中方法將在拋出異常的過程中結束。要是不希望方法就此結束,可以在方法內設置一個特殊的塊來捕獲異常。因爲在這個塊了“嘗試”各種方法調用,所以稱爲try塊。它是跟在try關鍵字之後的普通程序塊:

try{
// Code that might generate exception
}

對於不支持異常處理的程序語言,要想仔細檢查錯誤,就得在每個方法調用的前後加上設置和錯誤檢查的代碼,甚至在每次調用同一方法時也得這麼做。有了異常處理機制,可以把所有動作都放在try塊裏,然後只需在一個地方就可以捕獲所有異常。
3.2 異常處理程序
當然,拋出的異常必須在某處得到處理。這個“地點”就是異常處理程序,而且針對每個要捕獲的異常,得準備相應的處理程序。異常處理程序緊跟在try塊後,以關鍵字catch表示:

try{
 // Code that might generate exception
}catch(Type1 id1){
 // Handle exceptions of Type1
}catch(Type2 id2){
 // Handle exceptions of Type2
}catch(Type3 id3){
 // Handle exceptions of Type3
}
// etc...

每個catch子句看起來就像是接收一個且僅接受一個特殊類型的參數的方法。可以在處理程序的內部使用標識符,這與方法參數的使用很相似。有時可能用不到標識符,因爲異常的類型已經給了你足夠的信息來對異常進行處理,但標識符不可以省略。
異常處理程序必須緊跟在try塊之後。當異常被拋出時,異常處理機制將負責搜尋參數與異常類型相匹配的第一個處理程序。然後進入catch子句執行,此時任務異常得到了處理。一旦catch子句接受,則處理程序的查找過程結束。注意,只有匹配的catch子句才能得到執行,這與switch語句不同,switch語句需要在每一個case後面跟一個break,以避免執行後續的case子句。
注意在try塊的內部,許多不同的方法調用可能會產生類型相同的異常,而你只需要提供一個針對類型的異常處理程序。

4.創建自定義異常
要自己定義異常,必須從已有的異常類繼承,最好是選擇意思相近的異常類繼承。建立新的異常類型最簡單的方法就是讓編譯器爲你產生默認構造器。

public class SimpleExcetion extends Exception{}
public class TestException{
        public void f() throws SimpleExcetion{
            System.out.println("Throw SimpleExcetion from f()");
            throw new SimpleExcetion();
        }
        public static void main(String[] args) {
            TestException testException = new TestException();
            try {
                testException.f();
            } catch (SimpleExcetion e) {
                System.out.println("caught it");
            }
        }
}
輸出:Throw SimpleExcetion from f()
      caught it

編譯器創建了默認的構造器,它將自動調用基類的默認構造器。在本例中,你可以看到最重要的部分就是類名。
也可以爲異常類定義一個接受字符串參數的構造器:

public class MyException extends Exception{
    public MyException(){       
    }
    public MyException(String msg){
        super(msg);
    }
}
public class FullConstructors {
    public static void f() throws MyException{
        System.out.println("throwing myException from f()");
        throw new MyException();
    }
    public static void g() throws MyException{
        System.out.println("throwing myException from g()");
        throw new MyException("originated in g()");
    }
    public static void main(String[] args) {
        try {
            f();
        } catch (MyException e) {
            e.printStackTrace(System.out);
        }
        try {
            g();
        } catch (MyException e) {
            e.printStackTrace(System.out);
        }
    }
}
輸出:
throwing myException from f()
MyException
    at FullConstructors.f(FullConstructors.java:6)
    at FullConstructors.main(FullConstructors.java:14)
throwing myException from g()
MyException: originated in g()
    at FullConstructors.g(FullConstructors.java:10)
    at FullConstructors.main(FullConstructors.java:19)

兩個構造器定義了MyException類型對象的創建方式。對於第二個構造器,使用super關鍵字明確調用了其基類構造器,它接受一個字符串作爲參數。
在異常處理程序中,調用了在Throwable類聲明的printStackTrace()方法。就像從輸出中看到的,它將打印“從方法調用處直到一次拋出處”的方法調用序列。這裏,信息被送到了System.out,並自動地被捕獲和顯示輸出中。但是,如果調用默認版本:e.printStackTrace();則信息將被輸出到標準錯誤流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章