Java文法總結 – 異常

來源:互聯網
上載者:User
Java文法總結 - 異常

軟體開發中一個古老的說法是:80%的工作使用20%的時間。80%是指檢查和處理錯誤所付出的努力。在許多語言中,編寫檢查和處理錯誤的程式碼很乏味,並使應用程式代碼變得冗長。原因之一就是它們的錯誤處理方式不是語言的一部分。儘管如此,錯誤偵測和處理仍然是任何健壯應用程式最重要的組成部分。

Java提供了一種很好的機制,用強制規定的形式來消除錯誤處理過程中隨心所欲的因素:異常處理。它的優秀之處在於不用編寫特殊代碼檢測傳回值就能很容易地檢測錯誤。而且它讓我們把異常處理代碼明確地與異常產生代碼分開,代碼變得更有條理。異常處理也是Java中唯一正式的錯誤報表機制。

第一部分    異常

1、拋出異常。所有的標準異常類都有兩個構造器:一個是預設構造器,一個是帶參數的構造器,以便把相關資訊放入異常對象中。
    throw new NullPointerException();
    throw new NullPointerException("t = null");

2、如果有一個或者多個catch塊,則它們必須緊跟在try塊之後,而且這些catch塊必須互相緊跟著,不能有其他任何代碼。C++沒有這樣的限制,所以C++的異常處理處理不好就會寫得很亂,拋來拋去的。

3、使用try塊把可能出現異常的程式碼封裝含在其中,這麼做的好處是:處理某種指定的異常的代碼,只需編寫一次。作業沒寫完的同學到走廊罰站去,這符合我們處理問題的方式,不用挨個地告訴。

4、無論是否拋出異常,finally塊封裝的代碼總能夠在try塊之後的某點執行。
例子:
    try {
        return ;    
    }
    finally{
        System.out.print("You can't jump out of my hand!");    
    }

甚至你在try塊內用return語句想跳過去都不可以!finally內的輸出語句還是執行了!別想逃出我的手掌心!

5、catch塊和finally塊是可選的,你可以只使用try。但是這麼做有意思嗎?

6、推卸責任。Java允許你推卸責任,沒有必要從相應的try塊為每個可能的異常都編寫catch子句。Java2類庫中很多方法都會拋出異常,就是是把異常處理的許可權交給了我們使用者。畢竟,Java不知道你的單車被偷了之後,你會去報案還是會忍氣吞聲自認倒黴,或者偷別人的單車。我們需要這種處理異常的自由度。

7、調用棧。調用棧是程式執行以訪問當前方法的方法鏈。被調用的最後一個方法在棧的頂部,它將被最先執行完畢,然後彈出;第一個調用方法位於底部,也就是main函數。在catch子句中使用printStackTrace()方法打調用棧資訊是比較常用的定位異常的方法。printStackTrace()繼承自Throwable。

8、異常的傳播。在一個方法A中,如果一個異常沒有得到處理,它就會被自動拋到調用A方法的B方法中。如果B方法也沒有處理這個異常,他就會被繼續依次向上拋,直到main方法。如果main也沒有理會它,那麼異常將導致JVM停止,程式就中止了。你被同學揍了,先去告訴老師。老師不理你你就去告訴教導處主任,教導處主任也不管那隻能告訴校長,校長還不管!沒有比他更大的了,於是你崩潰了,學業中止了……下面這段程式記錄了悲慘的輟學曆史:
    class ExceptionDemo {
        static void student() throws Exception{
            teacher();
        }
        static void teacher() throws Exception{
            schoolmaster();
        }
        static void schoolmaster() throws Exception{
            throw new Exception();
        }
        public static void main(String[] args) {
            try {
            student();
        }
        catch (Exception e) {
            e.printStackTrace();
        }         
        }
    }
輸出結果是:
java.lang.Exception
    at ExceptionDemo.schoolmaster(ExceptionDemo.java:9)
    at ExceptionDemo.teacher(ExceptionDemo.java:6)
    at ExceptionDemo.student(ExceptionDemo.java:3)
    at ExceptionDemo.main(ExceptionDemo.java:13)
可以看出函數的調用棧,一級一級地哭訴……

9、異常的階層及Error。
        Object
        Throwable
    Error        Exception
Throwable繼承自Object,Error和Exception繼承自Throwable。Error比較特殊,它對應於我們常說的不可抗拒的外力,房屋中介的合約上總有一條,如遇不可抗拒的外力本合約中止,返還乙方押金。我不安地問:不可抗拒的外力指什嗎?中介回答:比如戰爭、彗星撞擊地球等。對Java來說Error是指JVM記憶體耗盡等這類不是程式錯誤或者其他事情引起的特殊情況。一般地,程式不能從Error中恢複,因此你可以能眼睜睜地看著程式崩潰而不必責怪自己。嚴格來講,Error不是異常,因為它不是繼承自Exception。

10、誰之錯?一般地,異常不是我們程式員的錯,不是程式設計上的缺陷。比如讀取一個重要檔案,這個檔案被使用者誤刪了;正上著網呢,網線被使用者的寵物咬斷了。為了程式的健壯性,我們盡量考慮出現可能性大的異常,並處理,但我們不能窮盡。

11、異常的捕獲之一。catch子句的參數是某種類型異常的對象,如果拋出的異常是該參數的子類,那麼這個異常將被它捕獲。也就是說被拋出的異常不會精確地尋找最匹配的捕獲者(catch子句),只要是它的繼承結構的直繫上層就可以捕獲它。

按照這個邏輯,catch(Exception e) 不就能捕獲所有的異常嗎?事實上,確實如此。 但是一般地,不建議使用這種一站式的異常處理。因為這樣就丟失了具體的異常資訊,不能為某個具體的異常編寫相應的異常處理代碼,失去了異常處理的意義。從哲學角度來講,具體問題要具體分析,能治百病的萬能藥一般都是無效的保健品。

Java在此處為什麼這麼設計呢?因為有另一種機制的存在,請看下條分解。

12、異常的捕獲之二。當拋出一個異常時,Java試圖尋找一個能捕獲它的catch子句,如果沒找到就會沿著棧向下傳播。這個過程就是異常匹配。Java規定:最具體的例外處理常式必須總是放在更普通例外處理常式的前面。這條規定再合理不過了,試想如果把catch(Exception e)放在最上面,那麼下面的catch子句豈不是永遠不能執行了?如果你非要把更普遍的異常處理放在前面,對不起,通不過編譯!雖然編譯器不會這樣報錯:“It is so stupid to do like that!”……

13、捕獲或聲明規則。如果在一個方法中拋出異常,你有兩個選擇:要麼用catch子句捕獲所有的異常,要麼在方法中聲明將要拋出的異常,否則編譯器不會讓你得逞的。
方案一:處理異常
    void ex(){
        try{
            throw new Exception();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
方案二:拋出去
    void ex() throws Exception{
        throw new Exception();
    }
比較一下行數就知道了,在代碼的世界裡推卸責任也是那麼簡單,一個throws關鍵字包含了多少人生哲理啊……現實生活中我們有很多角色,兒女、父母、學生、老師、老闆、員工……每個人都佔了幾條。可是你能盡到所有責任嗎?按照古代的孝道,父母尚在人世就不能遠行。各種責任是有矛盾的,顧此失彼啊。

但是這條規則有個特例。一個繼承自Exception名為RuntimeException的子類,也就是運行時異常,不受上述規則的限制。下面的代碼完全能編譯,只不過調用之後在運行時會拋出異常。
    void ex(){
        throw new RuntimeException();
    }

14、throw和thrwos關鍵字。throw用在方法體中拋出異常,後面是一個具體的異常對象。throws用在方法參數列表括弧的後面,用來聲明此方法會拋出的異常種類,後面跟著一個異常類。

15、非檢查異常。RuntimeException、Error以及它們的子類都是非檢查異常,不要求定義或處理非檢查異常。Java2類庫中有很多方法拋出檢查異常,因此會常常編寫例外處理常式來處理不是你編寫的方法產生的異常。這種機制強制開發人員處理錯誤,使得Java程式更加健壯,安全。

16、自訂異常類型。覺得現有的異常無法描述你想拋出的異常,ok!Java允許你自訂異常類型,只需要繼承Exception或者它的子類,然後換上有個性的名字。
    class NotEnoughMoney extends Exception {
        public NotEnoughMoney() {}
        public NotEnoughMoney(String msg) { super(msg); }
    }
希望大家在生活裡不要拋出類似的異常。

17、重新拋出異常。一個很無聊的話題,純粹的文法研究,實際意義不大。當catch子句捕獲到異常之後可以重新拋出,那麼它所在的方法必須聲明該異常。
    void ex() throws Exception{
        try {
            throw new Exception();
        }
        catch (Exception mex) {
            throw me;
        }    
    }

18、異常處理機制的效率。待補充……

19、終止與恢複模型。異常處理理論上有兩種模型:
一、終止模型。錯誤很關鍵且無法挽回,再執行下去也沒意義,只能中止。“羅密歐,我們分手吧!”“好吧,朱麗葉!”
二、恢複模型。經過錯誤修正重新嘗試調用原來出問題的方法。“羅密歐,我們分手吧!”“朱麗葉,我錯了!請再原諒我一次吧!”“好的,再原諒你最後一次!”
顯然我們更喜歡恢複模型,但在實際中,這種模式是不易實現和維護的。
例子:使用者輸入了非法的字元,分別按照兩種模式處理
一、終止模型。輸出出錯資訊而已,一旦使用者手一抖眼一花你的代碼就崩潰了
    double number;
    String sNumber = "";
    try {
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        sNumber = bf.readLine();
        number = Double.parseDouble(sNumber);    
    } catch (IOException ioe) {
        System.err.println("some IOException");
    } catch (NumberFormatException nfe) {
        System.err.println(sNumber + " is Not a legal number!");
    }
    //System.out.println(number);

二、恢複模型。小樣!不輸入正確的資料類型就別想離開!
    double number = 0;
    String sNumber = "";
    while(true){
        try {
            BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
            sNumber = bf.readLine();
            number = Double.parseDouble(sNumber);
            break;    //如果代碼能執行到這一行,就說明沒有拋出異常
        } catch (IOException ioe) {
            System.err.println("some IOException");
        } catch (NumberFormatException nfe) {
            System.err.println(sNumber + " is Not a legal number!");
        }
    }
    System.out.println(number);

直到使用者輸入正確的資訊才會被該代碼放過。這是一種簡單的恢複模型的實現,挺耐看的,我很喜歡!

20、try、catch、finally內變數的範圍和可見度。
在try塊內定義的變數,它在catch或者finally塊內都是無法訪問到的,並且在整個異常處理語句之外也是不可見的。
補充一點初始化:第一個例中最後一句被注釋掉了。number是在運行時由使用者輸入而初始化的,但是在編譯時間刻並沒有初始化,編譯器會抱怨的。

21、輸出異常資訊。捕捉到異常之後,通常我們會輸出相關的資訊,以便更加明確異常。
    catch (Exception mex) {
        System.err.println("caught a exception!");
    }
用標準錯誤流System.err比System.out要好。因為System.out也許會被重新導向,System.err則不會。

22、更進階的話題我會補充上的,但是我的肚子拋出了Hungry異常,我必須catch然後調用eat()方法補充能量。昨晚的魷魚蓋澆飯很好吃……
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.