在 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
}