1. 程式人生 > >java中try 與catch的使用的詳細說明

java中try 與catch的使用的詳細說明

try{
//程式碼區
}catch(Exception e){
//異常處理
}
程式碼區如果有錯誤,就會返回所寫異常的處理。

首先要清楚,如果沒有try的話,出現異常會導致程式崩潰。
而try則可以保證程式的正常執行下去,比如說:

try{
int i = 1/0;
}catch(Exception e){
........
}

一個計算的話,如果除數為0,則會報錯,如果沒有try的話,程式直接崩潰。用try的話,則可以讓程式執行下去,並且輸出為什麼出錯!
try catch 是捕捉try部分的異常,當你沒有trycatch的時候,如果出現異常則程式報錯,加上trycatch,出現異常程式正常執行,只是把錯誤資訊儲存到Exception裡,所以catch是用來提取異常資訊的,你可以在Catch部分加上一句System.out.println(e.ToString());,如果出現異常可以把異常打印出來

java的異常處理機制(try…catch…finally)

1 . 引子

try…catch…finally恐怕是大家再熟悉不過的語句了,而且感覺用起來也是很簡單,邏輯上似乎也是很容易理解。不過,我親自體驗的“教訓”告訴我,這個東西可不是想象中的那麼簡單、聽話。不信?那你看看下面的程式碼,“猜猜”它執行後的結果會是什麼?不要往後看答案、也不許執行程式碼看真正答案哦。如果你的答案是正確,那麼這篇文章你就不用浪費時間看啦。

public class TestException
{
    public TestException()
    {
    }
    boolean testEx() throws Exception
    {
        boolean ret = true;
        try
        {
            ret =testEx1();
        }
        catch (Exception e)
        {
            System.out.println("testEx,catch exception");
            ret =false;
            throwe;
        }
        finally
        {
            System.out.println("testEx,finally; return value=" + ret);
            returnret;
        }
    }
    boolean testEx1() throws Exception
    {
        boolean ret = true;
        try
        {
            ret =testEx2();
            if(!ret)
            {
                returnfalse;
            }
            System.out.println("testEx1,at the end of try");
            returnret;
        }
        catch (Exception e)
        {
            System.out.println("testEx1,catch exception");
            ret =false;
            throwe;
        }
        finally
        {
            System.out.println("testEx1,finally; return value=" + ret);
            returnret;
        }
    }
    boolean testEx2() throws Exception
    {
        boolean ret = true;
        try
        {
            int b =12;
            int c;
            for(int i = 2; i >= -2; i--)
            {
                c= b / i;
                System.out.println("i="+ i);
            }
            returntrue;
        }
        catch (Exception e)
        {
            System.out.println("testEx2,catch exception");
            ret =false;
            throwe;
        }
        finally
        {
            System.out.println("testEx2,finally; return value=" + ret);
            returnret;
        }
    }
    public static void main(String[] args)
    {
        TestException testException1 =new TestException();
        try
        {
            testException1.testEx();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

你的答案是什麼?是下面的答案嗎?

i=2
i=1
testEx2, catch exception
testEx2, finally; return value=false
testEx1, catch exception
testEx1, finally; return value=false
testEx, catch exception
testEx, finally; return value=false

如果你的答案真的如上面所說,那麼你錯啦。那就建議你仔細看一看這篇文章或者拿上面的程式碼按各種不同的情況修改、執行、測試,你會發現有很多事情不是原來想象中的那麼簡單的。
現在公佈正確答案:

i=2
i=1
testEx2, catch exception
testEx2, finally; return value=false
testEx1, catch exception
testEx1, finally; return value=false
testEx, catch exception
testEx, finally; return value=false

2 基礎知識

2.1 相關概念

例外是在程式執行過程中發生的異常事件,比如除0溢位、陣列越界、檔案找不到等,這些事件的發生將阻止程式的正常執行。為了加強程式的魯棒性,程式設計時,必須考慮到可能發生的異常事件並做出相應的處理。C語言中,通過使用if語句來判斷是否出現了例外,同時,呼叫函式通過被呼叫函式的返回值感知在被呼叫函式中產生的例外事件並進行處理。全程變數ErroNo常常用來反映一個異常事件的型別。但是,這種錯誤處理機制會導致不少問題。
Java通過面向物件的方法來處理例外。在一個方法的執行過程中,如果發生了例外,則這個方法生成代表該例外的一個物件,並把它交給執行時系統,執行時系統尋找相應的程式碼來處理這一例外。我們把生成例外物件並把它提交給執行時系統的過程稱為拋棄(throw)一個例外。執行時系統在方法的呼叫棧中查詢,從生成例外的方法開始進行回朔,直到找到包含相應例外處理的方法為止,這一個過程稱為捕獲(catch)一個例外。

2.2 Throwable類及其子類

用面向物件的方法處理例外,就必須建立類的層次。類 Throwable位於這一類層次的最頂層,只有它的後代才可以做為一個例外被拋棄。圖1表示了例外處理的類層次。
從圖中可以看出,類Throwable有兩個直接子類:Error和Exception。Error類物件(如動態連線錯誤等),由Java虛擬機器生成並拋棄(通常,Java程式不對這類例外進行處理);Exception類物件是Java程式處理或拋棄的物件。它有各種不同的子類分別對應於不同型別的例外。其中類RuntimeException代表執行時由Java虛擬機器生成的例外,如算術運算例外ArithmeticException(由除0錯等導致)、陣列越界例外ArrayIndexOutOfBoundsException等;其它則為非執行時例外,如輸入輸出例外IOException等。Java編譯器要求Java程式必須捕獲或宣告所有的非執行時例外,但對執行時例外可以不做處理。

2.3  異常處理關鍵字

Java的異常處理是通過5個關鍵字來實現的:try,catch,throw,throws,finally。JB的線上幫助中對這幾個關鍵字是這樣解釋的:

Throws: Lists the exceptions a method could throw. Throw: Transfers
control of the method to the exceptionhandler. Try: Opening
exception-handling statement. Catch: Captures the exception. Finally:
Runs its code before terminating the program.

2.3.1 try語句

try語句用大括號{}指定了一段程式碼,該段程式碼可能會拋棄一個或多個例外。

2.3.2 catch語句

catch語句的引數類似於方法的宣告,包括一個例外型別和一個例外物件。例外型別必須為Throwable類的子類,它指明瞭catch語句所處理的例外型別,例外物件則由執行時系統在try所指定的程式碼塊中生成並被捕獲,大括號中包含物件的處理,其中可以呼叫物件的方法。
catch語句可以有多個,分別處理不同類的例外。Java執行時系統從上到下分別對每個catch語句處理的例外型別進行檢測,直到找到型別相匹配的catch語句為止。這裡,型別匹配指catch所處理的例外型別與生成的例外物件的型別完全一致或者是它的父類,因此,catch語句的排列順序應該是從特殊到一般。
也可以用一個catch語句處理多個例外型別,這時它的例外型別引數應該是這多個例外型別的父類,程式設計中要根據具體的情況來選擇catch語句的例外處理型別。

2.3.3 finally語句

try所限定的程式碼中,當拋棄一個例外時,其後的程式碼不會被執行。通過finally語句可以指定一塊程式碼。無論try所指定的程式塊中拋棄或不拋棄例外,也無論catch語句的例外型別是否與所拋棄的例外的型別一致,finally所指定的程式碼都要被執行,它提供了統一的出口。通常在finally語句中可以進行資源的清除工作。如關閉開啟的檔案等。

2.3.4 throws語句

throws總是出現在一個函式頭中,用來標明該成員函式可能丟擲的各種異常。對大多數Exception子類來說,Java編譯器會強迫你宣告在一個成員函式中丟擲的異常的型別。如果異常的型別是Error或RuntimeException,或它們的子類,這個規則不起作用,因為這在程式的正常部分中是不期待出現的。如果你想明確地丟擲一個RuntimeException,你必須用throws語句來宣告它的型別。

2.3.5 throw語句

throw總是出現在函式體中,用來丟擲一個異常。程式會在throw語句後立即終止,它後面的語句執行不到,然後在包含它的所有try塊中(可能在上層呼叫函式中)從裡向外尋找含有與其匹配的catch子句的try塊。

3 關鍵字及其中語句流程詳解

3.1 try的巢狀

你可以在一個成員函式呼叫的外面寫一個try語句,在這個成員函式內部,寫另一個try語句保護其他程式碼。每當遇到一個try語句,異常的框架就放到堆疊上面,直到所有的try語句都完成。如果下一級的try語句沒有對某種異常進行處理,堆疊就會展開,直到遇到有處理這種異常的try語句。下面是一個try語句巢狀的例子。

class MultiNest {
    static void procedure() {
        try {
            int a =0;
            int b =42/a;
        } catch(java.lang.ArithmeticExceptione) {
            System.out.println("inprocedure, catch ArithmeticException: " + e);
        }
    }
    public static void main(String args[]) {
        try {
            procedure();
        } catch(java.lang. Exception e){
            System.out.println("inmain, catch Exception: " + e);
        }
    }
}

這個例子執行的結果為:
in procedure, catch ArithmeticException: java.lang.ArithmeticException: / byzero
成員函式procedure裡有自己的try/catch控制,所以main不用去處理ArrayIndexOutOfBoundsException;當然如果如同最開始我們做測試的例子一樣,在procedure中catch到異常時使用throwe;語句將異常丟擲,那麼main當然還是能夠捕捉並處理這個procedure丟擲來的異常。例如在procedure函式的catch中的System.out語句後面增加throwe;語句之後,執行結果就變為:
in procedure, catch ArithmeticException: java.lang.ArithmeticException: / byzero
in main, catch Exception: java.lang.ArithmeticException: / by zero

3.2 try-catch程式塊的執行流程以及執行結果

相對於try-catch-finally程式塊而言,try-catch的執行流程以及執行結果還是比較簡單的。
首先執行的是try語句塊中的語句,這時可能會有以下三種情況:
1.如果try塊中所有語句正常執行完畢,那麼就不會有其他的“動做”被執行,整個try-catch程式塊正常完成。
2.如果try語句塊在執行過程中碰到異常V,這時又分為兩種情況進行處理:

–>如果異常V能夠被與try相應的catch塊catch到,那麼第一個catch到這個異常的catch塊(也是離try最近的一個與異常V匹配的catch塊)將被執行;如果catch塊執行正常,那麼try-catch程式塊的結果就是“正常完成”;如果該catch塊由於原因R突然中止,那麼try-catch程式塊的結果就是“由於原因R突然中止(completesabruptly)”。
–>如果異常V沒有catch塊與之匹配,那麼這個try-catch程式塊的結果就是“由於丟擲異常V而突然中止(completesabruptly)”。

3.如果try由於其他原因R突然中止(completesabruptly),那麼這個try-catch程式塊的結果就是“由於原因R突然中止(completesabruptly)”。

3.3try-catch-finally程式塊的執行流程以及執行結果

try-catch-finally程式塊的執行流程以及執行結果比較複雜。
首先執行的是try語句塊中的語句,這時可能會有以下三種情況:
1.如果try塊中所有語句正常執行完畢,那麼finally塊的居於就會被執行,這時分為以下兩種情況:

–>如果finally塊執行順利,那麼整個try-catch-finally程式塊正常完成。
–>如果finally塊由於原因R突然中止,那麼try-catch-finally程式塊的結局是“由於原因R突然中止(completes abruptly)”

2.如果try語句塊在執行過程中碰到異常V,這時又分為兩種情況進行處理:

一、如果異常V能夠被與try相應的catch塊catch到,那麼第一個catch到這個異常的catch塊(也是離try最近的一個與異常V匹配的catch塊)將被執行;這時就會有兩種執行結果:
  • 如果catch塊執行正常,那麼finally塊將會被執行,這時分為兩種情況:

(1)如果finally塊執行順利,那麼整個try-catch-finally程式塊正常完成。
(2) 如果finally塊由於原因R突然中止,那麼try-catch-finally程式塊的結局是“由於原因R突然中止(completes abruptly)”
(3)如果catch塊由於原因R突然中止,那麼finally模組將被執行,分為兩種情況:
A. 如果如果finally塊執行順利,那麼整個try-catch-finally程式塊的結局是“由於原因R突然中止(completes abruptly)”。
B. 如果finally塊由於原因S突然中止,那麼整個try-catch-finally程式塊的結局是“由於原因S突然中止(completes abruptly)”,原因R將被拋棄。
(注意,這裡就正好和我們的例子相符合,雖然我們在testEx2中使用throw e丟擲了異常,但是由於testEx2中有finally塊,而finally塊的執行結果是complete abruptly的(別小看這個用得最多的return,它也是一種導致complete abruptly的原因之一啊——後文中有關於導致complete abruptly的原因分析),所以整個try-catch-finally程式塊的結果是“complete abruptly”,所以在testEx1中呼叫testEx2時是捕捉不到testEx1中丟擲的那個異常的,而只能將finally中的return結果獲取到。
如果在你的程式碼中期望通過捕捉被呼叫的下級函式的異常來給定返回值,那麼一定要注意你所呼叫的下級函式中的finally語句,它有可能會使你throw出來的異常並不能真正被上級呼叫函式可見的。當然這種情況是可以避免的,以testEx2為例:如果你一定要使用finally而且又要將catch中throw的e在testEx1中被捕獲到,那麼你去掉testEx2中的finally中的return就可以了。
這個事情已經在OMC2.0的MIB中出現過啦:伺服器的異常不能完全被反饋到客戶端。

  • 如果異常V沒有catch塊與之匹配,那麼finally模組將被執行,分為兩種情況:
    (1) 如果finally塊執行順利,那麼整個try-catch-finally程式塊的結局就是“由於丟擲異常V而突然中止(completes abruptly)”。
    (2) 如果finally塊由於原因S突然中止,那麼整個try-catch-finally程式塊的結局是“由於原因S突然中止(completes abruptly)”,異常V將被拋棄。
  • 如果try由於其他原因R突然中止(completesabruptly),那麼finally塊被執行,分為兩種情況:
    (1) 如果finally塊執行順利,那麼整個try-catch-finally程式塊的結局是“由於原因R突然中止(completes abruptly)”。
    (2) 如果finally塊由於原因S突然中止,那麼整個try-catch-finally程式塊的結局是“由於原因S突然中止(completes abruptly)”,原因R將被拋棄。

3.4 try-catch-finally程式塊中的return

從上面的try-catch-finally程式塊的執行流程以及執行結果一節中可以看出無論try或catch中發生了什麼情況,finally都是會被執行的,那麼寫在try或者catch中的return語句也就不會真正的從該函式中跳出了,它的作用在這種情況下就變成了將控制權(語句流程)轉到finally塊中;這種情況下一定要注意返回值的處理。
例如,在try或者catch中returnfalse了,而在finally中又return true,那麼這種情況下不要期待你的try或者catch中的return false的返回值false被上級呼叫函式獲取到,上級呼叫函式能夠獲取到的只是finally中的返回值,因為try或者catch中的return語句只是轉移控制權的作用。

3.5 如何丟擲異常

如果你知道你寫的某個函式有可能丟擲異常,而你又不想在這個函式中對異常進行處理,只是想把它丟擲去讓呼叫這個函式的上級呼叫函式進行處理,那麼有兩種方式可供選擇:
第一種方式:直接在函式頭中throws SomeException,函式體中不需要try/catch。比如將最開始的例子中的testEx2改為下面的方式,那麼testEx1就能捕捉到testEx2丟擲的異常了。

 boolean testEx2() throws Exception{
        boolean ret = true;
        int b=12;
        int c;
        for (int i=2;i>=-2;i--){
            c=b/i;
            System.out.println("i="+i);
        }
        return true;   
}

第二種方式:使用try/catch,在catch中進行一定的處理之後(如果有必要的話)丟擲某種異常。例如上面的testEx2改為下面的方式,testEx1也能捕獲到它丟擲的異常:

 boolean testEx2() throws Exception{
        boolean ret = true;
        try{
            intb=12;
            int c;
            for(int i=2;i>=-2;i--){
                c=b/i;
                System.out.println("i="+i);
            }
            returntrue;
        }catch (Exception e){
            System.out.println("testEx2,catch exception");
            Throwe;
        }
    }

第三種方法:使用try/catch/finally,在catch中進行一定的處理之後(如果有必要的話)丟擲某種異常。例如上面的testEx2改為下面的方式,testEx1也能捕獲到它丟擲的異常:

 boolean testEx2() throws Exception{
        boolean ret = true;
        try{
            intb=12;
            int c;
            for(int i=2;i>=-2;i--){
                c=b/i;
                System.out.println("i="+i);
                thrownew Exception("aaa");
            }
            returntrue;
        }catch(java.lang.ArithmeticException e){
            System.out.println("testEx2,catch exception");
            ret =false;
            thrownew Exception("aaa");
        }finally{
            System.out.println("testEx2,finally; return value="+ret);
        }
    }

4 關於abrupt completion

前面提到了complete abruptly(暫且理解為“突然中止”或者“異常結束”吧),它主要包含了兩種大的情形:abrupt completion of expressions and statements,下面就分兩種情況進行解釋。

4.1 Normal and Abrupt Completion of Evaluation

每一個表示式(expression)都有一種使得其包含的計算得以一步步進行的正常模式,如果每一步計算都被執行且沒有異常丟擲,那麼就稱這個表示式“正常結束(completenormally)”;如果這個表示式的計算丟擲了異常,就稱為“異常結束(completeabruptly)”。異常結束通常有一個相關聯的原因(associated reason),通常也就是丟擲一個異常V。
與表示式、操作符相關的執行期異常有:

**–>A class instance creation expression, array creation expression , orstring concatenation operatior expression throws an OutOfMemoryError if thereis insufficient memory available.
–>An array creation expression throws a NegativeArraySizeException if thevalue of any dimension expression is less than zero.
–>A field access throws a NullPointerException if the value of the objectreference expression is null.
–>A method invocation expression that invokes an instance method throws aNullPointerException if the target reference is null.
–>An array access throws a NullPointerException if the value of the arrayreference expression is null.
–>An array access throws an ArrayIndexOutOfBoundsException if the value ofthe array index expression is negative or greater than or equal to the lengthof the array.
–>A cast throws a ClassCastException if a cast is found to be impermissibleat run time.
–>An integer division or integer remainder operator throws anArithmeticException if the value of the right-hand operand expression is zero.
–>An assignment to an array component of reference type throws anArrayStoreException when the value to be assigned is not compatible with thecomponent type of the array.
**

4.2 Normal and Abrupt Completion of Statements

正常情況我們就不多說了,在這裡主要是列出了abrupt completion的幾種情況:

  • break, continue, and return 語句將導致控制權的轉換,從而使得statements不能正常地、完整地執行。
  • 某些表示式的計算也可能從java虛擬機器丟擲異常,這些表示式在上一小節中已經總結過了;一個顯式的的throw語句也將導致異常的丟擲。丟擲異常也是導致控制權的轉換的原因(或者說是阻止statement正常結束的原因)。
    如果上述事件發生了,那麼這些statement就有可能使得其正常情況下應該都執行的語句不能完全被執行到,那麼這些statement也就是被稱為是complete abruptly.

導致abrupt completion的幾種原因:

–>A break with no label
–>A break with a given label
–>A continue with no label
–>A continue with a given label
–>A return with no value
–>A return with a given value A
–>throw with a given value, including exceptions thrown by the Java virtualmachine

5 關於我們的程式設計的一點建議

弄清楚try-catch-finally的執行情況後我們才能正確使用它。
如果我們使用的是try-catch-finally語句塊,而我們又需要保證有異常時能夠丟擲異常,那麼在finally語句中就不要使用return語句了(finally語句塊的最重要的作用應該是釋放申請的資源),因為finally中的return語句會導致我們的throw e被拋棄,在這個try-catch-finally的外面將只能看到finally中的返回值(除非在finally中丟擲異常)。(我們需要記住:不僅throw語句是abruptcompletion的原因,return、break、continue等這些看起來很正常的語句也是導致abrupt completion的原因。)