资料不在于多,而在于精。好资料、好书,我们站在巨人的肩膀上前行,可以少走很多弯路。
通过搜索引擎找到自己需要的最好最权威信息,是一种很重要的能力。
Java源代码和官方资料Java™ Tutorials
Java异常体系结构,是一种分层/层次结构树模型。
异常的根类是 java.lang.Throwable
,核心数据结构/模型和实现都在于此类。了解她们对理解异常信息很关键。
其子类 java.lang.Exception、java.lang.RuntimeException、java.lang.Error 都是标签类。
java.lang.Throwable
Throwable
是异常的根类,核心数据结构/模型和实现都在于此类。了解她们对理解异常信息很关键。
/**
* The {@code Throwable} class is the superclass of all errors and
* exceptions in the Java language. Only objects that are instances of this
* class (or one of its subclasses) are thrown by the Java Virtual Machine or
* can be thrown by the Java {@code throw} statement. Similarly, only
* this class or one of its subclasses can be the argument type in a
* {@code catch} clause.
*
* For the purposes of compile-time checking of exceptions, {@code
* Throwable} and any subclass of {@code Throwable} that is not also a
* subclass of either {@link RuntimeException} or {@link Error} are
* regarded as checked exceptions.
*
* <p>Instances of two subclasses, {@link java.lang.Error} and
* {@link java.lang.Exception}, are conventionally used to indicate
* that exceptional situations have occurred. Typically, these instances
* are freshly created in the context of the exceptional situation so
* as to include relevant information (such as stack trace data).
*
* @author unascribed
* @author Josh Bloch (Added exception chaining and programmatic access to
* stack trace in 1.4.)
* @jls 11.2 Compile-Time Checking of Exceptions
* @since JDK1.0
*/
public class Throwable implements Serializable {
/**
* Specific details about the Throwable.
*/
private String detailMessage;
/**
* The throwable that caused this throwable to get thrown, or null if this
* throwable was not caused by another throwable, or if the causative
* throwable is unknown.
*/
private Throwable cause = this;
/**
* The stack trace, as returned by {@link #getStackTrace()}.
*/
private StackTraceElement[] stackTrace = UNASSIGNED_STACK;
/**
* The list of suppressed exceptions, as returned by {@link #getSuppressed()}.
*/
private List<Throwable> suppressedExceptions = SUPPRESSED_SENTINEL;
/** Caption for labeling causative exception stack traces */
private static final String CAUSE_CAPTION = "Caused by: ";
/** Caption for labeling suppressed exception stack traces */
private static final String SUPPRESSED_CAPTION = "Suppressed: ";
/**
* Constructs a new throwable with the specified detail message and
* cause. <p>Note that the detail message associated with
* {@code cause} is <i>not</i> automatically incorporated in
* this throwable's detail message.
*
* <p>The {@link #fillInStackTrace()} method is called to initialize
* the stack trace data in the newly created throwable.
*/
public Throwable(String message, Throwable cause) {
fillInStackTrace();
detailMessage = message;
this.cause = cause;
}
/**
* Returns a short description of this throwable.
* The result is the concatenation of:
* <ul>
* <li> the {@linkplain Class#getName() name} of the class of this object
* <li> ": " (a colon and a space)
* <li> the result of invoking this object's {@link #getLocalizedMessage}
* method
* </ul>
* If {@code getLocalizedMessage} returns {@code null}, then just
* the class name is returned.
*/
public String toString() {
String s = getClass().getName();
String message = getLocalizedMessage();
return (message != null) ? (s + ": " + message) : s;
}
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);
}
}
}
异常调用堆栈,最底下的"Caused by: "是问题的根源所在。
java.lang.StackTraceElement
其中 StackTraceElement
表示调用栈跟踪元素,了解其核心数据结构/模型对理解异常信息也很关键。
/**
* An element in a stack trace, as returned by {@link
* Throwable#getStackTrace()}. Each element represents a single stack frame.
* All stack frames except for the one at the top of the stack represent
* a method invocation. The frame at the top of the stack represents the
* execution point at which the stack trace was generated. Typically,
* this is the point at which the throwable corresponding to the stack trace
* was created.
*
* @since 1.4
* @author Josh Bloch
*/
public final class StackTraceElement implements java.io.Serializable {
private String declaringClass;
private String methodName;
private String fileName;
private int lineNumber;
/**
* Creates a stack trace element representing the specified execution
* point.
*/
public StackTraceElement(String declaringClass, String methodName,
String fileName, int lineNumber) {
this.declaringClass = Objects.requireNonNull(declaringClass, "Declaring class is null");
this.methodName = Objects.requireNonNull(methodName, "Method name is null");
this.fileName = fileName;
this.lineNumber = lineNumber;
}
/**
* Returns a string representation of this stack trace element. The
* format of this string depends on the implementation, but the following
* examples may be regarded as typical:
* <ul>
* <li>
* {@code "MyClass.mash(MyClass.java:9)"} - Here, {@code "MyClass"}
* is the <i>fully-qualified name</i> of the class containing the
* execution point represented by this stack trace element,
* {@code "mash"} is the name of the method containing the execution
* point, {@code "MyClass.java"} is the source file containing the
* execution point, and {@code "9"} is the line number of the source
* line containing the execution point.
* <li>
* {@code "MyClass.mash(MyClass.java)"} - As above, but the line
* number is unavailable.
* <li>
* {@code "MyClass.mash(Unknown Source)"} - As above, but neither
* the file name nor the line number are available.
* <li>
* {@code "MyClass.mash(Native Method)"} - As above, but neither
* the file name nor the line number are available, and the method
* containing the execution point is known to be a native method.
* </ul>
* @see Throwable#printStackTrace()
*/
public String toString() {
return getClassName() + "." + methodName +
(isNativeMethod() ? "(Native Method)" :
(fileName != null && lineNumber >= 0 ?
"(" + fileName + ":" + lineNumber + ")" :
(fileName != null ? "("+fileName+")" : "(Unknown Source)")));
}
}
异常日志解读示例
实践出真知识,才能真正地理解!
异常调用堆栈,最底下的"Caused by: "是问题的根源所在。
MethodA() -> MethodB() -> MethodC()-此处抛出了异常
异常调用堆栈
XxxException: message
at ...
at MethodA()
at ...
Caused by: XxxException: message
at MethodB()
Caused by: XxxException: message --问题的根源所在
at MethodC()
异常调用堆栈倒打,将最底下的"Caused by: "问题根源打印到最上面。
异常日志解读
com.alibaba.xxx.xxx.common.exception.ServiceException: waybill rule is null by DISTRIBUTOR_30474702
at com.alibaba.xxx.xxx.biz.common.WaybillAssistUtils.getExportWaybillRuleDO(WaybillAssistUtils.java:239)
第一行日志
com.alibaba.xxx.xxx.common.exception.ServiceException: waybill rule is null by DISTRIBUTOR_30474702
// 参考 java.lang.Throwable#toString
getClass().getName():com.alibaba.xxx.xxx.common.exception.ServiceException
getLocalizedMessage()=detailMessage:waybill rule is null by DISTRIBUTOR_30474702
第二行日志
at com.alibaba.xxx.xxx.biz.common.WaybillAssistUtils.getExportWaybillRuleDO(WaybillAssistUtils.java:239)
// 参考 java.lang.Throwable#printStackTrace(java.lang.Throwable.PrintStreamOrWriter)
// StackTraceElement[] trace = getOurStackTrace();
// for (StackTraceElement traceElement : trace)
// s.println("\tat " + traceElement);
"\tat ": at
traceElement:com.alibaba.xxx.xxx.biz.common.WaybillAssistUtils.getExportWaybillRuleDO(WaybillAssistUtils.java:239)
// 参考 java.lang.StackTraceElement#toString
getClassName() + "." + methodName:com.alibaba.xxx.xxx.biz.common.WaybillAssistUtils.getExportWaybillRuleDO
getClassName()=declaringClass:com.alibaba.xxx.xxx.biz.common.WaybillAssistUtils
methodName:getExportWaybillRuleDO
"(" + fileName + ":" + lineNumber + ")":(WaybillAssistUtils.java:239)
fileName:WaybillAssistUtils.java
lineNumber:239
进阶阅读资料
- Java语言规范和源代码
- 官方资料Java™ Tutorials - https://docs.oracle.com/javase/tutorial/
- Java™ Tutorials 的 Lesson: Exceptions 对异常体系讲解的通俗易懂 - https://docs.oracle.com/javase/tutorial/essential/exceptions/index.html
祝大家玩得开心!ˇˍˇ
简放,杭州