1. 程式人生 > >《Think in Java》閱讀筆記·第三卷

《Think in Java》閱讀筆記·第三卷

通過異常處理錯誤

異常情形:是指阻止當前方法或作用域繼續執行的問題。

監控區域:一段可能產生異常的程式碼區域,即try區域。

異常處理程式:處理異常的地點,即catch區域。

異常處理的兩種模型

終止模型:一旦丟擲異常,將無法返回產生異常處。

恢復模型:在產生異常時不丟擲異常,而是通過方法或者將try放到迴圈語句中以修補異常。

注意:恢復模型容易增強程式耦合性

自定義異常

import java.util.logging.Logger;

public class MyException {
    private static void f() throws CustomException{
        System.out
.println("f:"); throw new CustomException(); } private static void g(String msg)throws CustomException{ System.out.println("g:"); throw new CustomException(msg); } private static void h(){ int x,y,z; x=4; y=0; try { z=x/y; } catch
(Exception e) { e.printStackTrace(); } } public static void main(String... args) { try { f(); } catch (CustomException e) { e.printStackTrace(System.out); } try { g("msg"); } catch (CustomException e) { e.printStackTrace(System.out
); } h(); } } class CustomException extends Exception{ private Logger logger=Logger.getLogger("CustomException"); public CustomException(){ } public CustomException(String msg){ super(msg); } @Override public String getMessage() { //return super.getMessage(); return "This is my custom error info"; } }

當註釋掉return super.getMessage():

f:
thinkinjava.CustomException: This is my custom error info
at thinkinjava.MyException.f(MyException.java:7)
at thinkinjava.MyException.main(MyException.java:26)
g:
thinkinjava.CustomException: This is my custom error info
at thinkinjava.MyException.g(MyException.java:11)
at thinkinjava.MyException.main(MyException.java:32)
h:
java.lang.ArithmeticException: / by zero
at thinkinjava.MyException.h(MyException.java:19)
at thinkinjava.MyException.main(MyException.java:36)

當註釋掉return “This is my custom error info”:

f:
thinkinjava.CustomException
at thinkinjava.MyException.f(MyException.java:7)
at thinkinjava.MyException.main(MyException.java:26)
g:
thinkinjava.CustomException: msg
at thinkinjava.MyException.g(MyException.java:11)
at thinkinjava.MyException.main(MyException.java:32)
h:
java.lang.ArithmeticException: / by zero
at thinkinjava.MyException.h(MyException.java:19)
at thinkinjava.MyException.main(MyException.java:36)

getMessage類似於toString方法

異常與記錄日誌

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Logger;

public class LoggerException {
    private static  Logger logger=Logger.getLogger("LoggerException");
    private static void logprint(Exception e){
        StringWriter sw=new StringWriter();
        PrintWriter pw=new PrintWriter(sw);
        e.printStackTrace(pw);
        logger.severe(pw.toString());
    }
    public static void main(String...args){
        try {
            throw new NullPointerException();
        } catch (NullPointerException e) {
            logprint(e);
        }
    }
}

五月 01, 2018 9:31:36 下午 thinkinjava.LoggerException logprint
嚴重: [email protected]

throw:拋錯誤給上一層處理

public class Thrower {
    static void demoproc() {
        try {
            throw new NullPointerException("demo");
        } catch(NullPointerException e) {
            System.out.println("Caught inside demoproc.");
            throw e;
        }
    }
    public static void main(String args[]) {
        try {
            demoproc();
        } catch(NullPointerException e) {
            System.out.println("Recaught: " + e);
        }
    }
}

Caught inside demoproc.
Recaught: java.lang.NullPointerException: demo

棧軌跡

public class StackTracer {
    private static void a(){
        try{
            throw new Exception();
        }catch (Exception e){
            for(StackTraceElement stackTraceElement:e.getStackTrace())
                System.out.println(stackTraceElement.getMethodName());
        }
    }
    private static void b(){
       a();
    }
    private static void c(){
        b();
    }
    public static void main(String...args){
        a();
        System.out.println("-------------");
        b();
        System.out.println("-------------");
        c();

    }
}

輸出:

a
main
-------------
a
b
main
-------------
a
b
c
main

從棧底開始呼叫,類似於遞迴

若把當前異常重新丟擲,printStackTrace()顯示的仍然是原來異常丟擲點的呼叫棧資訊
    private static void  d() throws Exception{
        throw new Exception("throw new Exception from d");
    }

    private static void e() throws Exception{
        try{
            d();
        }catch (Exception e){
            System.out.println("e try catch");
            e.printStackTrace(System.out);
            throw e;
        }
    }

    private static void f() throws Exception{
        try{
            d();
        }catch (Exception e){
            System.out.println("f try catch");
            e.printStackTrace(System.out);
            throw (Exception) e.fillInStackTrace();
        }
    }
    public static void main(String...args){
        try {
            e();
        }catch (Exception e){
            System.out.println("main try catch from e");
            e.printStackTrace(System.out);
        }
        try {
            f();
        }catch (Exception e){
            System.out.println("main try catch from f");
            e.printStackTrace(System.out);
        }
    }

e try catch
java.lang.Exception: throw new Exception from d
at thinkinjava.StackTracer.d(StackTracer.java:19)
at thinkinjava.StackTracer.e(StackTracer.java:24)
at thinkinjava.StackTracer.main(StackTracer.java:44)
main try catch from e
java.lang.Exception: throw new Exception from d
at thinkinjava.StackTracer.d(StackTracer.java:19)
at thinkinjava.StackTracer.e(StackTracer.java:24)
at thinkinjava.StackTracer.main(StackTracer.java:44)
f try catch
java.lang.Exception: throw new Exception from d
at thinkinjava.StackTracer.d(StackTracer.java:19)
at thinkinjava.StackTracer.f(StackTracer.java:34)
at thinkinjava.StackTracer.main(StackTracer.java:51)
*main try catch from f
java.lang.Exception: throw new Exception from d
at thinkinjava.StackTracer.f(StackTracer.java:38)
at thinkinjava.StackTracer.main(StackTracer.java:51)

如最後一個輸出結果,其已經不再輸出原來的那個丟擲點了d了

若丟擲的是兩個不同的自定義異常,那麼就無法關聯,即第二次丟擲點打印出的棧軌跡不會有第一次的任何資訊,這是如果我們也要列印第一次丟擲點的棧軌跡,則需要用到異常鏈:

public class ExceptionLink {
    class MyException2 extends Exception{
        public MyException2(Throwable throwable){
            super(throwable);
        }
        public MyException2(){
            super();
        }
    }
    class MyException1 extends Exception{
        public  MyException1(){
            super();
        }
    }
    private void a() throws MyException1{
        throw new MyException1();
    }
    private  void b() throws MyException2{
        try {
            a();
        }catch (MyException1 exception1){
            System.out.println("b try catch");
            exception1.printStackTrace(System.out);
            throw new MyException2();
            //throw new MyException2(exception1);
        }
    }
    public static void main(String...args){
        ExceptionLink exceptionLink=new ExceptionLink();
        try {
            exceptionLink.b();
        } catch (MyException2 myException2) {
            System.out.println("main try catch");
            myException2.printStackTrace(System.out);
        }
    }
}

輸出:

b try catch
thinkinjava.ExceptionLink$MyException1
    at thinkinjava.ExceptionLink.a(ExceptionLink.java:18)
    at thinkinjava.ExceptionLink.b(ExceptionLink.java:22)
    at thinkinjava.ExceptionLink.main(ExceptionLink.java:32)
main try catch
thinkinjava.ExceptionLink$MyException2
    at thinkinjava.ExceptionLink.b(ExceptionLink.java:26)
    at thinkinjava.ExceptionLink.main(ExceptionLink.java:32)

如上在第二個丟擲點並沒有丟擲第一個丟擲點所丟擲的資訊,因為這是兩個不同異常類
若將註釋掉的那行取消掉註釋則會輸出:

b try catch
thinkinjava.ExceptionLink$MyException1
    at thinkinjava.ExceptionLink.a(ExceptionLink.java:18)
    at thinkinjava.ExceptionLink.b(ExceptionLink.java:22)
    at thinkinjava.ExceptionLink.main(ExceptionLink.java:32)
main try catch
thinkinjava.ExceptionLink$MyException2: thinkinjava.ExceptionLink$MyException1
    at thinkinjava.ExceptionLink.b(ExceptionLink.java:26)
    at thinkinjava.ExceptionLink.main(ExceptionLink.java:32)
Caused by: thinkinjava.ExceptionLink$MyException1
    at thinkinjava.ExceptionLink.a(ExceptionLink.java:18)
    at thinkinjava.ExceptionLink.b(ExceptionLink.java:22)
    ... 1 more

這便是異常鏈

finally語句在return語句中使用
try{
    return;
}finally{
    System.out.println("before return")
}

其會在返回前輸出”before return”

異常丟失

造成異常丟失的兩種情況:

try{
    throw new Exception1();
}finally{
    throw new Exception2();
}
try{
    throw new MyException();
}finally{
    return;
}

異常的限制

  1. 基類中的構造器和方法都聲明瞭丟擲異常,但在方法體內都沒丟擲異常,目的是讓其派生類在重寫該方法時強制捕獲該異常。

  2. 當一個基類和一個介面中都定義了相同的一個方法,但聲明瞭丟擲不同的異常。那麼在繼承了該基類和實現了該介面中的類中實現該方法,就不能丟擲在該類和該介面中該方法中宣告的任何異常,派生類可以不丟擲異常。

  3. 2中的情況無法限制構造器,因為在例項化派生類時會自動例項化基類構造器,所以派生類需要宣告基類構造器宣告的丟擲的異常。但是派生類構造器無法捕獲基類構造器的異常,因為在派生類構造器中super(…)必須處於最前端。

  4. 如下程式碼4,會編譯出錯,因為子類繼承了父類,然後覆蓋了父類的fun方法,但丟擲了父類方法沒有丟擲的異常。倘若編譯期沒有檢查到錯誤,在執行階段如果呼叫basic.fun()不用捕獲異常,但若Basic basic=new Child();basic.fun()就會需要捕獲異常。

  5. 如下程式碼5,c.fun()捕獲了MyException2的異常,而基類中的fun方法聲明瞭丟擲了MyException1異常,這是可行的,因為MyException2繼承於MyException1。

//4
public class Child extends Basic{
    //compile erro
    public void fun() throws MyException{}
}
class Basic{
    public void fun(){}
}

//5
public class MyException2 extends MyException1{}
public class Child extends Basic{
    public static final void main(String...args){
        Child c=new Child();
        try{
            c.fun();
        }catch(MyException2){}
    }
}
class Basic{
    public void fun() throws MyException1{}
}

異常匹配

catch(MyException)會捕獲MyException以及所有從它派生的異常

將被檢查異常轉換成不檢查異常

import java.io.IOException;

public class  TranstException{
    public TranstException(){

    }
    public static final void main(String...args){
        try{
            throwToOther(0);
        }catch(RuntimeException e){}//吞噬異常
        System.out.println("end");
    }
    private static void throwToOther(int i){
        try{
            switch(i){
                case 0:throw new IOException();
                case 1:throw new NullPointerException();
                case 2:throw new RuntimeException();
            }
            throw new IOException();
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

end

將每一個異常封裝成一個RuntimeException,然後通過getCause方法取出:

import java.io.IOException;

public class  TranstException{
    public TranstException(){

    }
    public static final void main(String...args){
        for(int i=0;i<3;i++){
            try{
                throwToOther(i);
            }catch (RuntimeException er){
                try{
                    throw er.getCause();
                }catch (IOException e){
                    System.out.println("IOException");
                }catch (NullPointerException e){
                    System.out.println("NullPointerException");
                }catch (RuntimeException e){
                    System.out.println("RuntimeException");
                } catch (Throwable throwable) {
                    System.out.println("Throwable");
                }
            }
        }
        System.out.println("end");
    }
    private static void throwToOther(int i){
        try{
            switch(i){
                case 0:throw new IOException();
                case 1:throw new NullPointerException();
                case 2:throw new RuntimeException();
            }
            throw new IOException();
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

IOException
NullPointerException
RuntimeException
end