JDK源码解析之 java.lang.Throwable
在 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 }總結
以上是生活随笔為你收集整理的JDK源码解析之 java.lang.Throwable的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SVN备份脚本
- 下一篇: Real提示“作为受限用户,您无足够的w