在 Java 中,所有的異常都有一個共同的祖先 Throwable(可拋出)。Throwable 指定代碼中可用異常傳播機制通過 Java 應用程序傳輸的任何問題的共性。
一、類定義
public class Throwable implements Serializable {}
- Serializable:可被序列化的標志接口
二、成員變量
//靜態變量
//這兩個變量主要用于序列化
private static class SentinelHolder {public static final StackTraceElement STACK_TRACE_ELEMENT_SENTINEL =new StackTraceElement("", "", null, Integer.MIN_VALUE);public static final StackTraceElement[] STACK_TRACE_SENTINEL =new StackTraceElement[] {STACK_TRACE_ELEMENT_SENTINEL};
}
//一個空的StackTraceElement[]數組,用來初始化或者作為返回值。
private static final StackTraceElement[] UNASSIGNED_STACK = new StackTraceElement[0];
//一個空的只讀List,同樣用于初始化
private static final List<Throwable> SUPPRESSED_SENTINEL =Collections.unmodifiableList(new ArrayList<Throwable>(0));
//前兩個用于作為錯誤信息,后兩個作為printStackTrace方法的說明前綴使用
private static final String NULL_CAUSE_MESSAGE = "Cannot suppress a null exception.";
private static final String SELF_SUPPRESSION_MESSAGE = "Self-suppression not permitted";
private static final String CAUSE_CAPTION = "Caused by: ";
private static final String SUPPRESSED_CAPTION = "Suppressed: ";
//用作getSuppressed方法的返回值(當suppressedExceptions沒有元素時)
private static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0];//實例變量
//用來保存棧信息的軌跡
private transient Object backtrace;
//描述這個異常的信息
private String detailMessage;
//描述這個異常由哪個Throwable導致,默認是this。
private Throwable cause = this;
//異常拋出位置的棧信息,每個StackTraceElement代表一個棧信息,默認指向靜態常量UNASSIGNED_STACK,代表棧信息為空。
private StackTraceElement[] stackTrace = UNASSIGNED_STACK;
//JDK 1.7引入的新特性。該List用來保存被屏蔽的異常對象,在try-catch語句中,如果try中拋出了異常,在執行流程轉移到方法棧上一層之前,finally語句塊會執行,但是,如果在finally語句塊中又拋出了一個異常,那么這個異常會覆蓋掉之前拋出的異常,這點很像finally中return的覆蓋。
private List<Throwable> suppressedExceptions = SUPPRESSED_SENTINEL;
三、構造方法
public Throwable() {fillInStackTrace();
}
/*** @param message 異常描述信息,該參數直接賦值給實例變量detailMessage*/
public Throwable(String message) {fillInStackTrace();detailMessage = message;
}
/*** @param message 異常描述信息,該參數直接賦值給實例變量detailMessage* @param cause 描述當前異常由哪個異常引發*/
public Throwable(String message, Throwable cause) {fillInStackTrace();detailMessage = message;this.cause = cause;
}
/*** @param cause 描述當前異常由哪個異常引發*/
public Throwable(Throwable cause) {fillInStackTrace();detailMessage = (cause==null ? null : cause.toString());this.cause = cause;
}
/*** @param message 異常描述信息,該參數直接賦值給實例變量detailMessage* @param cause 描述當前異常由哪個異常引發* @param enableSuppression 是否支持Suppress異常消息* @param writableStackTrace 是否調用fillInStackTrace使堆棧信息可以寫入*/
protected Throwable(String message, Throwable cause,boolean enableSuppression, boolean writableStackTrace) {if (writableStackTrace) {fillInStackTrace();} else {stackTrace = null;}detailMessage = message;this.cause = cause;if (!enableSuppression)suppressedExceptions = null;
}
Throwable
提供了4個public
構造器和1個protected
構造器(該構造器由JDK1.7引入)。4個public
構造器共同點就是都調用了fillInStackTrace
方法。
fillInStackTrace
會首先判斷stackTrace
是不是為null
,如果不為null
則會調用native
方法fillInStackTrace
獲取當前堆棧信息。那么什么時候為null
呢,答案是上面的protected
構造器可以指定writableStackTrace
為false
,這樣stackTrace
就為null
了,就不會調用fillInStackTrace
獲取堆棧信息。如果你不需要異常的棧信息,你也可以重寫這個方法,讓它直接返回this
,畢竟異常的爬棧是一個開銷比較大的操作。
四、常用方法
1、printStackTrace方法
printStackTrace
把傳入的輸入流用內部類WrappedPrintStream
或WrappedPrintWriter
包裝,主要用來實現printStackTrace
方法在打印堆棧信息時的線程安全。
public void printStackTrace() {printStackTrace(System.err);
}public void printStackTrace(PrintStream s) {printStackTrace(new WrappedPrintStream(s));
}public void printStackTrace(PrintWriter s) {printStackTrace(new WrappedPrintWriter(s));
}private void printStackTrace(PrintStreamOrWriter s) {Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());dejaVu.add(this);synchronized (s.lock()) {s.println(this);StackTraceElement[] trace = getOurStackTrace();for (StackTraceElement traceElement : trace)s.println("\tat " + traceElement);for (Throwable se : getSuppressed())se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);Throwable ourCause = getCause();if (ourCause != null)ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu);}
}
}
printStackTrace
把傳入的輸入流用內部類WrappedPrintStream
或WrappedPrintWriter
包裝,主要用來實現printStackTrace
方法在打印堆棧信息時的線程安全。
2、getMessage()和getLocalizedMessage()
默認條件下可以調用getMessage
或getLocalizedMessage
方法獲取detailMessage
public String getMessage() {return detailMessage;
}
public String getLocalizedMessage() {return getMessage();
}
3、getCause()和initCause()
通過構造器自定義cause。構造完成后,可以通過getCause
方法訪問獲取,如果沒有指定cause
,則返回null
。
在構造完成后,也可通過initCause方法修改:修改cause
的前提是必須在構造方法中沒有指定別的cause
(即默認條件下cause
為this
),否則會拋出IllegalStateException
異常。另外也不能修改cause
為this
。
public synchronized Throwable getCause() {return (cause == this ? null : cause);
}public synchronized Throwable initCause(Throwable cause) {if (this.cause != this)throw new IllegalStateException("Can't overwrite cause with " +Objects.toString(cause, "a null"), this);if (cause == this)throw new IllegalArgumentException("Self-causation not permitted", this);this.cause = cause;return this;
}
五、拓展
1.處理異常機制
在 Java 應用程序中,異常處理機制為:拋出異常,捕捉異常。
拋出異常:當一個方法出現錯誤引發異常時,方法創建異常對象并交付運行時系統,異常對象中包含了異常類型和異常出現時的程序狀態等異常信息。運行時系統負責尋找處置異常的代碼并執行。throws,throw
? 捕獲異常:在方法拋出異常之后,運行時系統將轉為尋找合適的異常處理器(exception handler)。潛在的異常處理器是異常發生時依次存留在調用棧中的方法的集合。當異常處理器所能處理的異常類型與方法拋出的異常類型相符時,即為合適 的異常處理器。運行時系統從發生異常的方法開始,依次回查調用棧中的方法,直至找到含有合適異常處理器的方法并執行。當運行時系統遍歷調用棧而未找到合適 的異常處理器,則運行時系統終止。同時,意味著Java程序的終止。try…catch
? 對于運行時異常、錯誤或可查異常,Java技術所要求的異常處理方式有所不同。
? 由于運行時異常的不可查性,為了更合理、更容易地實現應用程序,Java規定,運行時異常將由Java運行時系統自動拋出,允許應用程序忽略運行時異常。
? 對于方法運行中可能出現的Error,當運行方法不欲捕捉時,Java允許該方法不做任何拋出聲明。因為,大多數Error異常屬于永遠不能被允許發生的狀況,也屬于合理的應用程序不該捕捉的異常。
? 對于所有的可查異常,Java規定:一個方法必須捕捉,或者聲明拋出方法之外。也就是說,當一個方法選擇不捕捉可查異常時,它必須聲明將拋出異常。
? 能夠捕捉異常的方法,需要提供相符類型的異常處理器。所捕捉的異常,可能是由于自身語句所引發并拋出的異常,也可能是由某個調用的方法或者Java運行時 系統等拋出的異常。也就是說,一個方法所能捕捉的異常,一定是Java代碼在某處所拋出的異常。簡單地說,異常總是先被拋出,后被捕捉的。
? 任何Java代碼都可以拋出異常,如:自己編寫的代碼、來自Java開發環境包中代碼,或者Java運行時系統。無論是誰,都可以通過Java的throw語句拋出異常。
? 從方法中拋出的任何異常都必須使用throws子句。
? 捕捉異常通過try-catch語句或者try-catch-finally語句實現。
? 總體來說,Java規定:對于可查異常必須捕捉、或者聲明拋出。允許忽略不可查的RuntimeException和Error。
try { // 可能會發生異常的程序代碼
} catch (Type1 id1){ // 捕獲并處置try拋出的異常類型Type1
}
catch (Type2 id2){ //捕獲并處置try拋出的異常類型Type2
}