java異常處理(Try Catch Finally)

一 前言

java異常處理你們都不陌生,總的來講有下面兩點:java

1.拋出異常:throw exception數組

class SimpleException{
    public void a() throws Exception{
        throw new Exception();
    };
}

2.捕獲異常:微信

public class MyException {
    public static void main(String[] args){
        MyException e = new MyException();
        SimpleException se = new SimpleException();
        try {
            se.a();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }
}

class SimpleException{
    public void a() throws Exception{
        throw new Exception();
    };
}

本文將在此基礎上,更加深刻的談一些細節問題。app

二 自定義異常類

java語言爲咱們提供了不少異常類,可是有時候咱們爲了寫代碼的方便仍是要自定義的去創造異常類:this

class SimpleException extends Exception {};

建立好以後咱們可使用try catch捕獲它:編碼

public class MyException {
    public static void main(String[] args){
        MyException e = new MyException();
        try {
            e.a();
        } catch (SimpleException e1) {
            e1.printStackTrace();
        }
    }
    
    public void a() throws SimpleException{
        throw new SimpleException();
    }
}

class SimpleException extends Exception {};

咱們在MyException中定義了一個方法a(),讓它拋出SimpleException異常,而後咱們在main()中調用這個方法,並使用try catch捕獲了這個異常:翻譯

SimpleException
    at MyException.a(MyException.java:15)
    at MyException.main(MyException.java:8)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

Process finished with exit code 0

編譯執行後的結果,主要看前三行就好了。這裏着重說明幾點:
1.拋出異常類型的指定:(exception specification)
當咱們須要在一個方法中拋出一個異常時,咱們使用throw後加某異常類的實例,程序會在此向客戶端程序(調用這段代碼的程序)拋出對應異常並在此退出(至關於return)。另外須要注意的是,咱們必須在定義該方法的時候指明異常類型,好比下面這段代碼會拋出SimpleException異常code

public void a() throws SimpleException

2.拋出多個異常:接口

public void a() throws SimpleException,AException,BException{
        throw new SimpleException();
        
    }

不一樣的異常類之間用逗號隔開便可,在這種狀況下咱們沒必要須throw每一個異常類的實例(),可是客戶端代碼必需要catch到每一個異常類:ci

public class MyException {
    public static void main(String[] args){
        MyException e = new MyException();
        try {
            e.a();
        } catch (SimpleException e1) {
            e1.printStackTrace();
        } catch (BException e1) {
            e1.printStackTrace();
        } catch (AException e1) {
            e1.printStackTrace();
        }
    }

    public void a() throws SimpleException,AException,BException{
        throw new SimpleException();
        
    }
}



class SimpleException extends Exception {};
class AException extends Exception{}
class BException extends Exception{}

三 stack trace

不管是拋出異常,或者是捕獲處理異常,咱們的目的是爲了寫出更健壯的程序,這很大程度上依賴於java異常機制給咱們提供的異常信息,而它的載體就是stack trace
前面的代碼中咱們直接使用printStackTrace()打印出異常信息,其實咱們還可使用getStackTrace()方法來獲取StackTraceElement型的集合,若是你手頭有IDEA的話,你能夠先搜索出StackTraceElement類,能夠發現它實現了接口Serializable ,再看看它的類描述:

/**
 * An element in a stack trace, as returned by {@link
 * Throwable#getStackTrace()}.  Each element represents a single stack frame.
 * All stack frames except for the one at the top of the stack represent
 * a method invocation.  The frame at the top of the stack represents the
 * execution point at which the stack trace was generated.  Typically,
 * this is the point at which the throwable corresponding to the stack trace
 * was created.
 *
 * @since  1.4
 * @author Josh Bloch
 */

講的很清楚,這個類的每一個實例都是stack trace的一個元素,表明着一個stack frame,stack trace是由getStackTrace()方法返回的。後邊的我試着翻譯了幾遍,都以爲很差,仍是直接上代碼才能說清楚:

public class MyException {
    public static void main(String[] args){
        MyException e = new MyException();
        e.a();

    public void a(){
        try {
            throw new Exception();
        } catch (Exception e) {
            StackTraceElement[] ste = e.getStackTrace();
            System.out.println(ste.length);

        }
    }
}

咱們定義了方法a,讓它拋出Exception異常的同時捕獲它,而後咱們經過getStackTrace()方法獲得一個StackTraceElement型的數組,並打印出數組的長度:

7

Process finished with exit code 0

咱們把代碼稍微改一下,不在a中捕獲異常了,咱們從新定義一個方法b,讓它在調用a的同時將異常捕獲:

public class MyException {
    public static void main(String[] args){
        MyException e = new MyException();
        e.b();
    }

    public void b(){
        try {
            a();
        } catch (Exception e) {
            StackTraceElement[] ste = e.getStackTrace();
            System.out.println(ste.length);
        }
    }

    public void a() throws Exception{
        throw new Exception();
    }
}

結果以下:

8

Process finished with exit code 0

別急,咱們再來看點有趣的:

public class MyException {
    public static void main(String[] args){
        MyException exception = new MyException();
        try {
            exception.c();
        } catch (Exception e) {
            StackTraceElement[] ste = e.getStackTrace();
            System.out.println(ste.length);
            System.out.println("---------------------------------------------------------------");
            for (StackTraceElement s : e.getStackTrace()){
                System.out.println(s.getClassName()+":method "+s.getMethodName()+" at line"+s.getLineNumber());
            }
            System.out.println("---------------------------------------------------------------");

        }

    }

 public void c() throws Exception{
        try {
            a();
        }catch (Exception e){
            throw e;
        }
    }

    public void a() throws Exception{
        throw new Exception();
    }
}

下面是結果:

8
---------------------------------------------------------------
MyException:method a at line43
MyException:method c at line39
MyException:method main at line9
sun.reflect.NativeMethodAccessorImpl:method invoke0 at line-2
sun.reflect.NativeMethodAccessorImpl:method invoke at line57
sun.reflect.DelegatingMethodAccessorImpl:method invoke at line43
java.lang.reflect.Method:method invoke at line606
com.intellij.rt.execution.application.AppMain:method main at line144
---------------------------------------------------------------

Process finished with exit code 0

也就是說,getStackTrace()返回一個棧,它包含從調用者(main())到初始拋出異常者(a())的一些基本信息 ,在上面的代碼中,咱們在c方法中調用a方法時捕獲異常並經過throws將其再次拋出(rethrow),調用c方法的方法能夠捕獲並處理異常,也能夠選擇繼續拋出讓更高層次的調用者(靠近棧底)處理。rethrow雖然很方便,但存在着一些問題,咱們看下面這段代碼:

public class MyException {
    public static void main(String[] args){
        MyException exception = new MyException();
        try {
            exception.c();
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }

    }

    public void c() throws Exception{
        try {
            a();
        }catch (Exception e){
            throw e;
        }
    }

    public void a() throws Exception{

        throw new Exception("Exception from a()");
    }
}
java.lang.Exception: Exception from a()
    at MyException.a(MyException.java:40)
    at MyException.c(MyException.java:30)
    at MyException.main(MyException.java:21)

咱們在c中從新拋出e,在main中使用 e.printStackTrace()打印出來,能夠看到打印出來stack trace仍是屬於a的,若是咱們想把stack trace變成c的能夠這麼寫:

public class MyException {
    public static void main(String[] args){
        MyException exception = new MyException();

        try {
            exception.c();
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }

    }

    public void c() throws Exception{
        try {
            a();
        }catch (Exception e){
//            throw e;
            throw (Exception)e.fillInStackTrace();
        }
    }

    public void a() throws Exception{

        throw new Exception("Exception from a()");
    }
}
java.lang.Exception: Exception from a()
    at MyException.c(MyException.java:22)
    at MyException.main(MyException.java:10)

四 異常鏈 Exception chaining

先來看一個場景:

public class TestException {
    public static void main(String[] args){
        TestException testException = new TestException();
        try {
            testException.c();
        } catch (CException e) {
            e.printStackTrace();
        }
    }

    public void a() throws AException{
        AException aException = new AException("this is a exception");
        throw  aException;
    }

    public void b() throws BException{
        try {
            a();
        } catch (AException e) {
            throw new BException("this is b exception");
        }
    }

    public void c() throws CException{
        try {
            b();
        } catch (BException e) {
            throw new CException("this is c exception");
        }
    }
}

class AException extends Exception{
    public AException(String msg){
        super(msg);
    }
}

class BException extends Exception{
    public BException(String msg){
        super(msg);
    }
}

class CException extends Exception{
    public CException(String msg){
        super(msg);
    }
}

建立了三個異常類AException、BException、CException,而後在a()中拋出AException,在b()中捕獲AException並拋出BException,最後在c()中捕獲BException並拋出CException,結果打印以下:

CException: this is c exception
    at TestException.c(TestException.java:31)
    at TestException.main(TestException.java:8)

好,咱們只看到了CException的信息,AException,BException的異常信息已丟失,這時候異常鏈的做用就出來了,看代碼:

public class TestException {
    public static void main(String[] args){
        TestException testException = new TestException();
        try {
            testException.c();
        } catch (CException e) {
            e.printStackTrace();
        }
    }

    public void a() throws AException{
        AException aException = new AException("this is a exception");
        throw  aException;
    }

    public void b() throws BException{
        try {
            a();
        } catch (AException e) {
//            throw new BException("this is b exception");
            BException bException = new BException("this is b exception");
            bException.initCause(e);
            throw bException;
        }
    }

    public void c() throws CException{
        try {
            b();
        } catch (BException e) {
//            throw new CException("this is c exception");
            CException cException = new CException("this is c exception");
            cException.initCause(e);
            throw cException;
        }
    }
}

class AException extends Exception{
    public AException(String msg){
        super(msg);
    }
}

class BException extends Exception{
    public BException(String msg){
        super(msg);
    }
}

class CException extends Exception{
    public CException(String msg){
        super(msg);
    }
}

咱們用initCause()方法將異常信息給串聯了起來,結果以下:

CException: this is c exception
    at TestException.c(TestException.java:35)
    at TestException.main(TestException.java:8)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: BException: this is b exception
    at TestException.b(TestException.java:24)
    at TestException.c(TestException.java:32)
    ... 6 more
Caused by: AException: this is a exception
    at TestException.a(TestException.java:15)
    at TestException.b(TestException.java:21)
    ... 7 more

Process finished with exit code 0

五 後記

其實關於java異常處理還有不少須要探討的地方,可是因爲我經驗有限,還不能體會的太深入,最經常使用的也就是

try {
            ...
        }catch (Exception e){
           ... 
        }finally {
          //無論異常會不會被捕捉或者處理都會執行的代碼,如關閉IO操做 
        }

可是不管如何咱們仍是要感謝java給咱們提供的異常機制,它好似一個長者,時不時給咱們指引道路,也讓咱們在編碼的時候沒有那麼無聊:)


個人微信號是aristark,歡迎交流指正!

相關文章
相關標籤/搜索