既然Throwable是异常处理机制的核心,那么,我们就来分析下它的源码来看看它是如何实现的。
进行分析前,我们可以先想想如果让我们实现一个异常处理机制,我们需要它做什么?
1. 发生异常终止程序执行,
2. 选择备选方案继续程序执行,
3. 可以显示程序出现具体位置。
异常终止,跳转等这些都是虚拟机来进行管理的,通过关键字,我们无从更深的考究。但是,异常的输出,我们可以通过源码来看看他的过程。
首先我们需要获取发生异常的具体位置信息。虚拟机的本地方法栈为我们提供了获取某个线程执行的方法调用深度以及方法信息和执行位置信息接口。
/** * Returns the number of elements in the stack trace (or 0 if the stack * trace is unavailable). * * package-protection for use by SharedSecrets. */ native int getStackTraceDepth(); /** * Returns the specified element of the stack trace. * * package-protection for use by SharedSecrets. * * @param index index of the element to return. * @throws IndexOutOfBoundsException if {@code index < 0 || * index >= getStackTraceDepth() } */ native StackTraceElement getStackTraceElement(int index);
printStackTrace方法具体实现
private void printStackTrace(PrintStreamOrWriter s) { // Guard against malicious overrides of Throwable.equals by // using a Set with identity equality semantics. Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>()); dejaVu.add(this); synchronized (s.lock()) { // Print our stack trace s.println(this); StackTraceElement[] trace = getOurStackTrace(); for (StackTraceElement traceElement : trace) s.println(" at " + traceElement); // Print suppressed exceptions, if any for (Throwable se : getSuppressed()) se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, " ", dejaVu); // Print cause, if any Throwable ourCause = getCause(); if (ourCause != null) ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu); } }
从中我们可以看到主要设计到三部分的输出,第一个是执行过程位置信息,第二个是提供的异常信息输出,第三个就是异常信息输出。
⚠️ 红线部分可以看出通过使用自带输出工具显示需要加锁,可以看出使用自带异常输出效率是很低的。
执行过程信息:
private synchronized StackTraceElement[] getOurStackTrace() { // Initialize stack trace field with information from // backtrace if this is the first call to this method if (stackTrace == UNASSIGNED_STACK || (stackTrace == null && backtrace != null) /* Out of protocol state */) { int depth = getStackTraceDepth(); stackTrace = new StackTraceElement[depth]; for (int i=0; i < depth; i++) stackTrace[i] = getStackTraceElement(i); } else if (stackTrace == null) { return UNASSIGNED_STACK; } return stackTrace; }
提供的异常信息:
public final synchronized void addSuppressed(Throwable exception) { if (exception == this) throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE, exception); if (exception == null) throw new NullPointerException(NULL_CAUSE_MESSAGE); if (suppressedExceptions == null) // Suppressed exceptions not recorded return; if (suppressedExceptions == SUPPRESSED_SENTINEL) suppressedExceptions = new ArrayList<>(1); suppressedExceptions.add(exception); }
异常信息:
public synchronized Throwable getCause() { return (cause==this ? null : cause); }
好处:
通过对源码分析我们可以更加直观知道异常信息是怎么输出的,这样如果我们想基于异常信息进行一些程序的个性化定制也成为一重可能,比如日志搜集的使用。