1. 程式人生 > >Java異常及異常處理

Java異常及異常處理

Java異常簡介

Java異常是Java提供的一種識別及響應錯誤的一致性機制。
Java異常機制可以使程式中異常處理程式碼和正常業務程式碼分離,保證程式程式碼更加優雅,並提高程式健壯性。按照程式碼的預先設定的異常處理邏輯,針對性地處理異常,讓程式盡最大可能恢復正常並繼續執行,且保持程式碼的清晰。
Java中的異常可以是函式中的語句執行時引發的,也可以是程式設計師通過throw 語句手動丟擲的,只要在Java程式中產生了異常,就會用一個對應型別的異常物件來封裝異常,JRE就會試圖尋找異常處理程式來處理異常。

Java異常的分類和類結構圖

Java標準庫內建了一些通用的異常,這些類以Throwable為頂層父類。

Throwable又派生出Error類和Exception類

錯誤:Error類以及他的子類的例項,代表了JVM本身的錯誤,比如OutOfMemoryError、ThreadDeath等。錯誤不能被程式設計師通過程式碼處理,Error很少出現。

異常:Exception以及他的子類,代表程式執行時傳送的各種不期望發生的事件。可以被Java異常處理機制使用,是異常處理的核心。

Java提供了兩類主要的異常:runtimeException和checkedException (檢查和非檢查是對於javac來說的)

非執行時異常(checkedException):主要是指IO異常、SQL異常等。對於這種異常,JVM要求我們必須對其進行

catch處理,所以,面對這種異常,不管我們是否願意,都是要寫一大堆的catch塊去處理可能出現的異常。

執行時異常(runtimeException):我們一般不處理,javac在編譯時,不會提示和發現這樣的異常,不要求在程式處理這些異常。當出現這類異常的時候程式會由虛擬機器接管。比如,我們從來沒有去處理過NullPointerException、ArithmeticException、ArrayIndexOutOfBoundsException而且這異常還是最常見的異常之一。出現執行時異常的時候,程式會將異常一直向上拋,一直拋到遇到處理程式碼,如果沒有catch塊進行處理,到了最上層,如果是多執行緒就有Thread.run()丟擲,如果不是多執行緒那麼就由main.run()丟擲。丟擲之後,如果是執行緒,那麼該執行緒也就終止了,如果是主程式,那麼該程式也就終止了。

 

異常導圖:

Throwable類中的常用方法 :

getCause():返回丟擲異常的原因。如果 cause 不存在或未知,則返回 null。

getMessage():返回異常的訊息資訊。

printStackTrace():物件的堆疊跟蹤輸出至錯誤輸出流,作為欄位 System.err 的值。

Java異常機制用到的幾個關鍵字try、catch、finally、throw、throws。
• try    -- 用於監聽。將要被監聽的程式碼(可能丟擲異常的程式碼)放在try語句塊之內,當try語句塊內發生異常時,異常就被丟擲。
• catch   -- 用於捕獲異常。catch用來捕獲try語句塊中發生的異常。
• finally  -- finally語句塊總是會被執行。它主要用於回收在try塊裡開啟的物力資源(如資料庫連線、網路連線和磁碟檔案)。
• throw   -- 用於丟擲異常。
• throws   -- 用在方法簽名中,用於宣告該方法可能丟擲的異常。

異常處理的基本語法:

try…catch…finally語句塊

try{

     //try塊中放可能發生異常的程式碼。

}catch(SQLException SQLexception){

    //用於捕獲可能出現的異常並對異常進行一定的處理,或者這異常型別的子類。

}catch(Exception exception){

    //...

}finally{

   //無論異常是否發生,異常是否匹配被處理,finally都會執行。

   //finally主要做一些清理工作,如流的關閉,資料庫連線的關閉等。

}

try、catch、finally三個語句塊應注意的問題 :

1、try塊中的區域性變數和catch塊中的區域性變數(包括異常變數),以及finally中的區域性變數,他們之間不可共享使用。

2、try、catch、finally三個語句塊均不能單獨使用,三者可以組成 try...catch...finally、try...catch、try...finally三種結構,catch語句可以有一個或多個,finally語句最多一個。

3、try、catch、finally三個程式碼塊中變數的作用域為程式碼塊內部,分別獨立而不能相互訪問。如果要在三個塊中都可以訪問,則需要將變數定義到這些塊的外面。

4、多個catch塊時候,只會匹配其中一個異常類並執行catch塊程式碼,而不會再執行別的catch塊,並且匹配catch語句的順序是由上到下。

通常捕獲異常catch的時候最大catch到Exception這個類就為止了,當然這能夠處理大部分的異常情況。

但是值得注意的是,Exception不能捕捉到所有的異常。比如InvocationTargetException

解決辦法:catch(Throwable t){ }

throws 函式宣告

throws宣告如果一個方法內部的程式碼會丟擲檢查異常(checked exception),而方法自己又沒有完全處理掉,則javac保證你必須在方法的簽名上使用throws關鍵字宣告這些可能丟擲的異常,否則編譯不通過。

throws是另一種處理異常的方式,它不同於try…catch…finally,throws僅僅是將函式中可能出現的異常向呼叫者宣告,而自己則不具體處理。

採取這種異常處理的原因可能是:方法本身不知道如何處理這樣的異常,或者說讓呼叫者處理更好,呼叫者需要為可能發生的異常負責。

public void throwsException() throws ExceptionType1 , ExceptionType2 ,ExceptionTypeN

{

  //throwsException內部可以丟擲 ExceptionType1 , ExceptionType2 ,ExceptionTypeN 類的異常,或者他們的子類的異常物件。

}

異常的注意事項:

1、當子類重寫父類的帶有 throws宣告的函式時,其throws宣告的異常必須在父類異常的可控範圍內——用於處理父類的throws方法的異常處理器,必須也適用於子類的這個帶throws方法 。這是為了支援多型。

例如,父類方法throws 的是2個異常,子類就不能throws 3個及以上的異常。父類throws IOException,子類就必須throws IOException或者IOException的子類。

2、Java程式可以是多執行緒的。每一個執行緒都是一個獨立的執行流,獨立的函式呼叫棧。如果程式只有一個執行緒,那麼沒有被任何程式碼處理的異常 會導致程式終止。如果是多執行緒的,那麼沒有被任何程式碼處理的異常僅僅會導致異常所在的執行緒結束。

也就是說,Java中的異常是執行緒獨立的,執行緒的問題應該由執行緒自己來解決,而不要委託到外部,也不會直接影響到其它執行緒的執行。

常見的ERROR:

Error類包括一些嚴重的程式不能處理的系統錯誤類,如記憶體溢位、虛擬機器錯誤、棧溢位等。這類錯誤一般與硬體有關,與程式本身無關,通常由系統進行處理,程式本身無法捕獲和處理。

  1. OutOfMemoryError記憶體溢位一般是出現在申請了較多的記憶體空間沒有釋放的情形。

    2.StackOverflowError堆疊溢位錯誤。當一個應用遞迴呼叫的層次太深而導致堆疊溢位時丟擲該錯誤。

          

        

 

finally塊:

finally塊不管異常是否發生,只要對應的try執行了,則它一定也執行。只有一種方法讓finally塊不執行:System.exit()。因此finally塊通常用來做資源釋放操作:關閉檔案,關閉資料庫連線等等。

良好的程式設計習慣是:在try塊中開啟資源,在finally塊中清理釋放這些資源。

   @Test
    public void testfinally() throws Exception {
//        returnCall();
       System.out.println(finallyRet());
    }
    /**
     *在 try塊中即便有return,break,continue等改變執行流的語句,finally也會執行。
     * 但是如果丟擲了異常未捕獲,則不會執行finally。如果有catch,但是未捕獲到,則任然會執行 
    finally塊。
     * finally中的return 會覆蓋 try 或者catch中的返回值。
     * @throws Exception
     */
    private void returnCall() throws Exception {
        try {
//          return 1;
            for (int i = 0; i < 3; i++) {
//                break;
                continue;
            }
            throw new Exception("finally不會執行");
        } finally {
            System.out.print("finally");
            //  return 3;
        }
    }

    /**
     * finally中的return會抑制(消滅)前面try或者catch塊中的異常
     * @return
     */
    private int finallyRet() {
        try {
            int a = 666 / 0;
        } catch (Exception e) {
            throw new Exception("我將被忽略,因為下面的finally中使用了return");
        } finally {
            return 6;
        }
    }


    @Test
    public void finallyThrow() throws Exception {
        try{
          int result = 666/0;
        }catch (Exception e){
            throw new Exception("會被覆蓋掉");
        }finally {
            throw new Exception("finally中的異常會覆蓋(消滅)前面try或者catch中的異常");
        }
    }

 

 

異常進行處理及原則總結

(1)儘量避免出現runtimeException 。例如對於可能出現空指標的程式碼,帶使用物件之前一定要判斷一下該物件是否為空,必要的時候對runtimeException也進行try catch處理。

(2)進行try catch處理的時候要在catch程式碼塊中對異常資訊進行記錄,通過呼叫異常類的相關方法獲取到異常的相關資訊,返回到web端,不僅要給使用者良好的使用者體驗,也要能幫助程式設計師良好的定位異常出現的位置及原因。

(3)能處理就早處理,丟擲不去還不能處理的就想法消化掉或者轉換為RuntimeException處理。

4)對於一個應用系統來說,應該有自己的一套異常處理框架,這樣當異常發生時,也能得到統一的處理風格。