Java源码侦探--异常体系解读Throwable

2017-01-13 15:02:29来源:csdn作者:wangyang1354人点击

第七城市

Throwable类是整个异常体系类的父级类,当然最终追根溯源到底的父类当然要归于Object类。Throwable类实现了Serializable接口,表示Throwable可以进行序列化,继承自Object类,他的子类主要是Error和Exception类还有一个StackRecorder类(不是很常见)。

那么这里有几个问题要思考:

1. 为什么序列化?

2. 源码中并没有看到继承Object这个行为,但是默认是继承了怎么做到的?

3. 这两个子类又是干什么的?为什么不能直接在Throwable一个类来处理?

序列化问题

序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。

三种情况:

1. 将对象存储再硬盘上。

2. 将对象通过网络传输。

3. 通过RMI远程调用等方式传输对象的时候。

在这三种情况下,是需要进行序列化然后传输的。

在Throwable类中使用输出流来进行输出,并把其对象作为输出流对象,这就需要必须实现序列化接口,使得其可以进行序列化,才能作为输出流中的对象进行输出。

private synchronized void writeObject(ObjectOutputStream s)
throws IOException {
// Ensure that the stackTrace field is initialized to a
// non-null value, if appropriate.As of JDK 7, a null stack
// trace field is a valid value indicating the stack trace
// should not be set.
getOurStackTrace();StackTraceElement[] oldStackTrace = stackTrace;
try {
if (stackTrace == null)
stackTrace = SentinelHolder.STACK_TRACE_SENTINEL;
s.defaultWriteObject();
} finally {
stackTrace = oldStackTrace;
}
}

继承Object类

1. 在编译时,如果类没有继承的父类的话,会自动为其加入继承父类Object的相关的类的编译信息,这样在后面虚拟器进行解释执行的时候,按照存在父类进行处理就可以了。

2. 在虚拟机进行执行的时候,如果仍然存在没有父类的类,仍然会默认其父类为Object。

第一种情况属于再编译器进行处理,第二种情况属于在虚拟机上面做适当的处理。

子类Error和Exception

Error主要是用于表示java和虚拟机内部的异常信息,而Exception异常则是由于程序中可能存在各种的问题,是需要使用者去注意和捕获的异常。

从异常类的设计中体会到,设计者的抽象思维与设计水平令人叹服,通过一个类去抽象出所有异常中通用的方法与表示形式以及其表达的实体结构,而且通过继承的方式对异常这个领域做一个水平划分,将其切分为Error和Exception两种平行的异常类型,然后,这两者将再次作为各自类型的异常的父类,因为每一种异常同样是存在不同的分类,再次创建一系列的类去继承上面的两种异常派生出新的异常类型划分。这样不断的继承下去,逐步的细化到每一种具体的异常类型,形成一个丰富的异常类族。

从扩展性上而言,由于Throwable实现的是异常类中通用的部分,那么,如果再有特殊的异常分类的话,可以通过继承Throwable的方式去扩展该异常体系,当然,我们最常用的可能不会涉及到直接继承Throwable。

源码解读

默认是空的StackTrace的节点数组初始化为空的stack,getOurStackTrace()方法实现的主要是获取当前节点异常的信息,获取栈上面的异常信息,遍历每一个异常信息,赋值给stackTrace进行保存。

private static final StackTraceElement[] UNASSIGNED_STACK = new StackTraceElement[0];private StackTraceElement[] stackTrace = UNASSIGNED_STACK;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;
}

获取到栈异常信息以后,输出异常信息,对该数组进行遍历,输出异常的位置。


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("/tat " + traceElement);// Print suppressed exceptions, if any
for (Throwable se : getSuppressed())
se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "/t", dejaVu);// Print cause, if any
Throwable ourCause = getCause();
if (ourCause != null)
ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu);
}
}

getSuppressed方法如下:

public final synchronized Throwable[] getSuppressed() {
if (suppressedExceptions == SUPPRESSED_SENTINEL ||
suppressedExceptions == null)
return EMPTY_THROWABLE_ARRAY;
else
return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY);
}getCause()方法如下: public synchronized Throwable getCause() {
return (cause==this ? null : cause);
}

其中,getSuppressed方法返回了被抑制(可能是try住的)的所有的异常信息,然后getCause()方法返回的是异常的原因,其中cause是一个Throwable属性,初始化为其本身this, 如果这个throwable中cause要么是抛出异常的类,要么是null,如果是Throwable类本身的话,那么只能说明Throwable类没有初始化完毕。

这个cause属性使得java可以通过链式的结构来组织异常信息,通过cause指向其下一个异常的抛出类。依次构成链状的结构。

下面这段代码中最后几行中,就展示了这种链式的结构通过递归的形式遍历并输出的过程。


private void printEnclosedStackTrace(PrintStreamOrWriter s,
StackTraceElement[] enclosingTrace,
String caption,
String prefix,
Set<Throwable> dejaVu) {
assert Thread.holdsLock(s.lock());
if (dejaVu.contains(this)) {
s.println("/t[CIRCULAR REFERENCE:" + this + "]");
} else {
dejaVu.add(this);
// Compute number of frames in common between this and enclosing trace
StackTraceElement[] trace = getOurStackTrace();
int m = trace.length - 1;
int n = enclosingTrace.length - 1;
while (m >= 0 && n >=0 && trace[m].equals(enclosingTrace[n])) {
m--; n--;
}
int framesInCommon = trace.length - 1 - m;// Print our stack trace
s.println(prefix + caption + this);
for (int i = 0; i <= m; i++)
s.println(prefix + "/tat " + trace[i]);
if (framesInCommon != 0)
s.println(prefix + "/t... " + framesInCommon + " more");// Print suppressed exceptions, if any
for (Throwable se : getSuppressed())
se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION,
prefix +"/t", dejaVu);// Print cause, if any
Throwable ourCause = getCause();
if (ourCause != null)
ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, prefix, dejaVu);
}
}

转载请注明出处:http://blog.csdn.net/wangyang1354/article/details/53438117


第七城市

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台