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

Java——異常處理

上一個 分別是 類型轉換 冒號 意思 參數 ack span log

1、java提供的異常不可能預見所有的問題,所以需要自己定義異常類,必須從已有的異常類繼承,最好選擇意思相近的異常類繼承。

class MyException extends Exception{}

public class Tree1 {
    
    public static void f() throws MyException{
        System.out.println("throws MyException from f()");
        throw new MyException();
    }
        
    public static void
main (String[] args) { try { f(); }catch(MyException e){ System.out.println("caught it"); } } }

try塊中的代碼將被監控,catch將會接受來自try塊的異常。這裏的f()方法將會拋出一個 MyException類的異常,然後catch將會接收到這個異常,並輸出caught it4

所以輸出結果為:

throws MyException from f()
caught it

可以為異常類定義一個接受字符串參數的構造器:

class MyException extends Exception{
    public MyException() {}
    public MyException(String msg) {
        super(msg);
    }
}
public class Tree1 {
    public static void f() throws MyException{
        System.out.println("throws MyException from f()");
        throw new MyException();
    }
        
    
public static void main (String[] args) { try { f(); }catch(MyException e){ e.printStackTrace(System.out); } } }

這樣的輸出是:

throws MyException from f()
MyException
    at Tree1.f(Tree1.java:11)
    at Tree1.main(Tree1.java:16)

在異常類的定義中的第二個構造器中使用了super關鍵字明確調用了其基類構造器,它接受一個字符串作為參數。

在異常處理程序中,調用了Throwable類聲明的printStackTrace()方法,就像輸出中看到的這樣,它將會打印“從方法調用處直到異常拋出處” 的方法調用序列,這裏信息發送到了System.out,如果使用默認的e.printStackTrace();則信息將被輸出到標準錯誤流。

2、Exception類型的方法

class MyException extends Exception{
    public MyException() {}
    public MyException(String msg) {
        super(msg);
    }
}
class MySecondException extends Exception{
    public MySecondException() {}
    public MySecondException(String msg) {
        super(msg);
    }
}
public class Tree1 {
    public static void f() throws MyException {
        System.out.println("throws MyException from f()");
        throw new MyException("name");
    }
    
    public static void g() throws MySecondException {
        System.out.println("throws MySecondException from g()");
        throw new MySecondException("name2");
    }
        
    public static void main (String[] args){
        try {
            f();
//            g();
        }catch(Exception e){
            System.out.println(e.getMessage());
            System.out.println(e);
            System.out.println(e.getLocalizedMessage());
        }
    }
}

這裏定義了兩個繼承自Exception的異常類,分別是MyException、MySecondException,由兩個方法f()、g()拋出,如果在try塊中只有f();,輸出結果為:

throws MyException from f()
name
MyException: name
name

Throwable類的getMessage()方法返回的是Exception的詳細消息字符串,就是拋出這個異常時候傳入的字符串,

而getLocalizedMessage()方法返回的是 Exception的本地化描述。

toString()方法返回 此對象的類的 name ": "(冒號和一個空格)

然後如果把上面代碼g();的前面的//刪掉,就是try塊中將運行f()和g()兩個方法,輸出結果為:

throws MyException from f()
name
MyException: name
name

和剛才一樣這是因為catch接收到異常後就會結束try塊中的程序運行,所以g()方法並沒有被運行,所以如果先運行g()的結果就是:

throws MySecondException from g()
name2
MySecondException: name2
name2

3、棧軌跡

public class Tree1 {
    public static void f(){
        try {
            throw new Exception();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    
    public static void g() {f();}
    public static void h() {g();}
        
    public static void main (String[] args){
        f();        
        g();
        h();
    }
}

輸出結果為:

java.lang.Exception
    at Tree1.f(Tree1.java:17)
    at Tree1.main(Tree1.java:27)
java.lang.Exception
    at Tree1.f(Tree1.java:17)
    at Tree1.g(Tree1.java:23)
    at Tree1.main(Tree1.java:28)
java.lang.Exception
    at Tree1.f(Tree1.java:17)
    at Tree1.g(Tree1.java:23)
    at Tree1.h(Tree1.java:24)
    at Tree1.main(Tree1.java:29)

這裏的調用的printStackTrace(),是Throwable類的方法,這個方法會將Throwable對象的棧軌跡信息打印到標準錯誤輸出流上,第一行是異常類的tostring()方法輸出的內容,後面幾行的內容都是之前通過fillInStackTrace()方法保存的內容。

java.lang.Exception
    at Tree1.f(Tree1.java:17)
    at Tree1.main(Tree1.java:27)

在這個例子中,在方法f()中拋出異常,在main方法中捕獲異常,並且打印棧軌跡信息。因此,輸出依次展示了f—>main的過程。還打印了拋出錯誤,捕獲錯誤的行數、類的名字。

java.lang.Exception
    at Tree1.f(Tree1.java:17)
    at Tree1.g(Tree1.java:23)
    at Tree1.main(Tree1.java:28)

這裏就是在方法f()中拋出異常,方法g()中調用了f(),然後和上一個一樣了,第三個也同樣。

public class Tree1 {
    public static void f(){
        try {
            throw new Exception();
        }catch(Exception e){
            for(StackTraceElement ste: e.getStackTrace()) {            
                System.out.println(ste);

            }
        }
    }
    
    public static void g() {f();}
    public static void h() {g();}
        
    public static void main (String[] args){
        f();
        System.out.println("------------------");
        g();
        System.out.println("------------------");
        h();
    }
}

這個例子中調用了getStackTrace()方法,這個方法返回StackTraceElement類的一個對象。其輸出內容為:

Tree1.f(Tree1.java:17)
Tree1.main(Tree1.java:30)
------------------
Tree1.f(Tree1.java:17)
Tree1.g(Tree1.java:26)
Tree1.main(Tree1.java:32)
------------------
Tree1.f(Tree1.java:17)
Tree1.g(Tree1.java:26)
Tree1.h(Tree1.java:27)
Tree1.main(Tree1.java:34)

如果這樣使用:

public class Tree1 {
    public static void f(){
        try {
            throw new Exception();
        }catch(Exception e){
            for(StackTraceElement ste: e.getStackTrace()) {            
                System.out.print(ste.getMethodName() + "  " );
                System.out.println(ste.getLineNumber());
            }
        }
    }
    
    public static void g() {f();}
    public static void h() {g();}
        
    public static void main (String[] args){
        f();
        System.out.println("------------------");
        g();
        System.out.println("------------------");
        h();
    }
}

調用StackTraceElement中的方法getMethodName()和getLineNumber()就可以值獲取棧軌跡中的方法 和行:

f  17
main  30
------------------
f  17
g  26
main  32
------------------
f  17
g  26
h  27
main  34

所以,其實StackTraceElement是一個棧軌跡元素的數組。將這些棧軌跡元素保存在一個數組中。每個元素對應棧的一個棧幀。數組的第一個元素保存的是棧頂元素,也就是上面的f。最後一個元素保存的棧底元素,也就是main。

public class Tree1 {
    public static void f() throws Exception{
            throw new Exception();
    }
    
    public static void g() throws Exception {
            try {
                f();
            }catch(Exception e){
                e.printStackTrace();
                throw e;
                }
    }
        
    public static void main (String[] args){
        try {
            g();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

4、重新拋出異常

這裏f()函數中拋出一個異常,然後g()方法中捕獲了這個異常並打印異常棧軌跡Stack Trace,然後又再拋出了剛剛的異常,mai方法中捕獲 了這個異常並打印異常棧軌跡Stack Trace,所以結果為:

java.lang.Exception
    at Tree1.f(Tree1.java:16)
    at Tree1.g(Tree1.java:21)
    at Tree1.main(Tree1.java:30)
java.lang.Exception
    at Tree1.f(Tree1.java:16)
    at Tree1.g(Tree1.java:21)
    at Tree1.main(Tree1.java:30)

也就是說,捕獲到異常又立即拋出,在上級方法調用中再次捕獲這個異常,打印的棧軌跡信息是一樣的。額,其實只看代碼也知道,因為這裏只是把剛剛異常e又拋出一次,

public class Tree1 {
    public static void f() throws Exception{
            throw new Exception();
    }
    
    public static void g() throws Exception {
            try {
                f();
            }catch(Exception e){
                e.printStackTrace();
                throw (Exception)e.fillInStackTrace();
                }
    }
        
    public static void main (String[] args){
        try {
            g();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

throw (Exception)e.fillInStackTrace();

這一行調用了Exception的fillInStackTrace()方法,首先要知道其實這個方法並不是來自於Exception類。Exception類本身除了定義了幾個構造器之外,所有的方法都是從其父類繼承過來的。而和異常相關的方法都是從java.lang.Throwable類繼承過來的,所以其實fillInStackTrace()是Throwable類的方法,然後fillInStackTrace()方法就是將當前線程當前狀態下的軌跡棧的狀態保存進Throwabe中,就是更新的意思。

public Throwable fillInStackTrace()

這是這個方法的原型所以它的返回值是一個Throwable類對象,所以使用它更新的時候需要強制類型轉換。

throw (Exception)e.fillInStackTrace();

所以輸出內容為:

java.lang.Exception
    at Tree1.f(Tree1.java:16)
    at Tree1.g(Tree1.java:21)
    at Tree1.main(Tree1.java:30)
java.lang.Exception
    at Tree1.g(Tree1.java:24)
    at Tree1.main(Tree1.java:30)

這次第二個打印的棧軌跡信息就沒有f了。

5、異常鏈

class MyException1 extends Exception{} 


class MyException2 extends Exception{}

public class Tree1 {
    
    public static void f() throws MyException1{
        throw new MyException1();
    }
    
    public static void g() throws MyException2{
        try {
            f();
        } catch (MyException1 e) {
            e.printStackTrace();
            throw new MyException2();
        }
    }

        
    public static void main (String[] args){
        try {
            g();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

這裏定義了兩個異常類,f()方法拋出了MyException1,g()方法捕獲了這個異常並打印了異常的棧軌鏈,然後拋出了另一個異常MyException2也打印了異常的棧軌鏈,所以輸出結果為:

MyException1
    at Tree1.f(Tree1.java:16)
    at Tree1.g(Tree1.java:21)
    at Tree1.main(Tree1.java:31)
MyException2
    at Tree1.g(Tree1.java:24)
    at Tree1.main(Tree1.java:31)

這沒什麽毛病,但是常常想要在捕獲一個異常後拋出另一個異常,並且希望把原始異常的信息保存下來,這就需要將原始異常的信息包裝在新的異常中,這被稱為異常鏈

class MyException1 extends Exception{} 


class MyException2 extends Exception{
    MyException2(Throwable throwable){
        super(throwable);
    }
    MyException2(){
        super();
    }
}
public class Tree1 {
    
    public static void f() throws MyException1{
        throw new MyException1();
    }
    
    public static void g() throws MyException2{
        try {
            f();
        } catch (MyException1 e) {
            e.printStackTrace();
            throw new MyException2(e);
        }
    }

        
    public static void main (String[] args){
        try {
            g();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

這樣的輸出結果就是:

MyException1
    at Tree1.f(Tree1.java:16)
    at Tree1.g(Tree1.java:21)
    at Tree1.main(Tree1.java:31)
MyException2: MyException1
    at Tree1.g(Tree1.java:24)
    at Tree1.main(Tree1.java:31)
Caused by: MyException1
    at Tree1.f(Tree1.java:16)
    at Tree1.g(Tree1.java:21)
    ... 1 more

這樣的定義:

public class MyException2 extends Exception{  
    //定義異常的原因  
    public MyException2(String message){  
        super(message);  
    }  
  
    //定義異常原因,並攜帶原始的異常  
    public MyException2(String message,Throwable cause){  
        super(message,cause);  
    }  
  
    //保留原始異常信息  
    publicMyException2(Throwable cause){  
        super(cause);  
    }  
}  

就能將原始異常傳遞給新異常

或是:

class MyException1 extends Exception{
} 


class MyException2 extends Exception{
    MyException2(Throwable throwable){
        super(throwable);
    }
    MyException2(){
        super();
    }
}

class MyException3 extends Exception{
    MyException3(Throwable throwable){
        super(throwable);
    }
    MyException3(){
        super();
    }
}

public class Tree1 {
    
    public static void f() throws MyException1{
        throw new MyException1();
    }
    
    public static void g() throws MyException2{
        try {
            f();
        } catch (MyException1 e) {
//            e.printStackTrace();
            throw new MyException2(e);
        }
    }
    
    public static void h() throws MyException3{
        try {
            g();
        } catch (MyException2 e) {
//            e.printStackTrace();
            throw new MyException3(e);
        }
    }

        
    public static void main (String[] args){
        try {
            h();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

這裏值輸出了最後一個異常的棧軌跡

MyException3: MyException2: MyException1
    at Tree1.h(Tree1.java:44)
    at Tree1.main(Tree1.java:51)
Caused by: MyException2: MyException1
    at Tree1.g(Tree1.java:35)
    at Tree1.h(Tree1.java:41)
    ... 1 more
Caused by: MyException1
    at Tree1.f(Tree1.java:27)
    at Tree1.g(Tree1.java:32)
    ... 2 more

其中還包括了MyException1和MyException2 的信息

Exception

Java——異常處理