#定义(百度百科)
异常:程序在运行过程中发生由于外部问题(如硬件错误、输入错误)等导致的程序异常事件。
(在Java等面向对象的编程语言中)异常本身是一个对象,产生异常就是产生了一个异常对象
#异常与错误的区别(百度百科)
异常都是运行时的。编译时产生的不是异常,而是错误(Error)。
需要注意的是,程序设计导致的错误(Error)不属于异常(Exception)。
#异常处理机制的体系结构
Throwable:是所有Errors和Exceptions的superclass
|- Error:系统内部错误,这类错误由系统进行处理,程序本身无需获取处理
|- Exception:可处理的异常
|- 非运行时异常
|- IOException
|- EOFException
|- FileNotFoundException
|- MalformedURLException
|- UnknownHostException
|- ClassNotFoundException
|- CloneNotSupported
|- RuntimeException:可以捕获的异常,也可以不捕获的异常;是those异常的超类
|- ArithmeticException
|- ClassCastException
|- IllegalArgumentException
|- IllegalStateException
|- IndexOutOfBoundsException
|- NoSuchElementException
|- NullPointerException
捕获:
通过try…catch语句进行捕获异常。
通过throw抛出异常,throws向上一级调用方法抛出异常。
#带着问题去探讨
1)为什么需要序列化
2)默认继承Object,怎么做到?
#问题解析
####序列化
定义(百度百科):序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象
目的:
1、以某种存储形式使自定义对象持久化;(硬盘储存)
2、将对象从一个地方传递到另一个地方。(网络传输)
3、使程序更具维护性。(RMI远程调用)
####默认继承Object类
1、在编译时如果没有继承的父类的话,会自动为其加入object超类的相关编译信息,这样在后续虚拟机的解释的时候会识别到其继承了Object超类
2、在虚拟机解析的时候如果没有检测Object超类还是会默认其父类为Object
第一种属于编译器再处理,第二种属于虚拟机适当处理
#异常源码剖析
####异常源码剖析之ArithmeticException(其它的举一反三)
######ArithmeticException.class
package java.lang;
/**
* 异常抛出条件:当发生异常算术条件时抛出。例如,一个整数“除零”抛出该类的实例
*/
public class ArithmeticException extends RuntimeException {
private static final long serialVersionUID = 2256477558314496007L;
/**
*算术异常构造函数没有细节信息
*/
public ArithmeticException() {
super();
}
/**
*算术异常构造函数有细节信息
*
* @param s 详细的消息.
*/
public ArithmeticException(String s) {
super(s);
}
}
RuntimeException.class
package java.lang;
/**
* RuntimeException是其它那些能够在Java虚拟机正常运行期间被抛出的异常的superclass。
* RuntimeException及其子类属于未检查的异常。如果Method及构造函数的执行可以抛出未
* 检查的异常并在Method或构造函数边界之外传播,则不需要在方法或构造函数的抛出字句中声明这些异常。
*/
public class RuntimeException extends Exception {
static final long serialVersionUID = -7034897190745766939L;
/*
* 构造一个新的运行时异常,其细节消息为null。
* cause没有被初始化,随后可以通过调用initCause对cause进行初始化
*/
public RuntimeException() {
super();
}
public RuntimeException(String message) {
super(message);
}
public RuntimeException(String message, Throwable cause) {
super(message, cause);
}
public RuntimeException(Throwable cause) {
super(cause);
}
/*
*构造一个新的运行时异常,其中包含指定的详细信息、原因、启用或禁用的抑制以及启用或禁用的可写堆栈跟踪。
* @param message 细节消息
* @param cause 原因,允许使用{@code null} value,并指示原因 nonexistent 或 unknown
* @param enableSuppression 启用或禁用抑制
* @param writableStackTrace 启用或禁用可写堆栈的跟踪
*/
protected RuntimeException(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
######Exception.class
package java.lang;
public class Exception extends Throwable {
static final long serialVersionUID = -3387516993124229948L;
public Exception() {
super();
}
public Exception(String message) {
super(message);
}
public Exception(String message, Throwable cause) {
super(message, cause);
}
public Exception(Throwable cause) {
super(cause);
}
protected Exception(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
######Throwable.class
package java.lang;
import java.io.*;
import java.util.*;
public class Throwable implements Serializable {
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -3042686055658047285L;
//Native code保存了些这槽中的堆栈回溯
private transient Object backtrace;
//关于可抛出异常的具体细节。例如,对于FileNotFoundException,其中包含无法找到的文件的名称。
private String detailMessage;
//Holder类仅用于延迟初始化被序列化的sentinel(哨兵) object
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};
}
//空堆栈的共享值
private static final StackTraceElement[] UNASSIGNED_STACK = new StackTraceElement[0];
/**
* 如果这个字段等于Throwable本身,说明这个Throwable的cause还没有被初始化
* 如果cause这个抛出项不是由另一个Throwable抛出项或者是一个未知的Throwable,那cause=null
*/
private Throwable cause = this;
//默认为空堆栈共享值:未赋值堆栈
private StackTraceElement[] stackTrace = UNASSIGNED_STACK;
//设置这个静态字段会对一些java.util类引入一个可接受的初始化依赖项
private static final List<Throwable> SUPPRESSED_SENTINEL =
Collections.unmodifiableList(new ArrayList<Throwable>(0));
//该列表(抑制异常)被初始化为零元素不可修改的sentinel(哨兵)列表。
private List<Throwable> suppressedExceptions = SUPPRESSED_SENTINEL;
/** 试图抑制空异常的Message. */
private static final String NULL_CAUSE_MESSAGE = "Cannot suppress a null exception.";
/** 试图抑制自己的Message. */
private static final String SELF_SUPPRESSION_MESSAGE = "Self-suppression not permitted";
/** Caused by:用于标注cause异常堆栈跟踪 */
private static final String CAUSE_CAPTION = "Caused by: ";
/** Suppressed:用于标记被抑制的异常堆栈跟踪 */
private static final String SUPPRESSED_CAPTION = "Suppressed: ";
public Throwable() {
fillInStackTrace();
}
public Throwable(String message) {
fillInStackTrace();
detailMessage = message;
}
public Throwable(String message, Throwable cause) {
fillInStackTrace();
detailMessage = message;
this.cause = cause;
}
public Throwable(Throwable cause) {
fillInStackTrace();
detailMessage = (cause==null ? null : cause.toString());
this.cause = cause;
}
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 的详细消息字符串
public String getMessage() {
return detailMessage;
}
//创建此 throwable 的本地化描述。子类可以重写此方法,以便生成特定于语言环境的消息。对于不重写此方法的子类,默认实现返回与 getMessage() 相同的结果。
public String getLocalizedMessage() {
return getMessage();
}
/**
* getCause返回的是异常的原因,cause为Throwable本身的一个属性,cause要么是抛出异常类要么是null,如果是
*Throwable本身的话说明没有初始化完毕。这个cause带来的好处是可以使得java通过链式的结构来组织异常信息,
* 不断的指向下一个异常抛出类,最后一个必为Throwable其本身,构成一条长长的链。
* /
public synchronized Throwable getCause() {
return (cause==this ? null : cause);
}
/**
*
*错误示例:
* try {
* lowLevelOp();
* } catch (LowLevelException le) {
* throw (HighLevelException)
* new HighLevelException().initCause(le); // Legacy constructor
* }
* 将此 throwable 的 cause 初始化为指定值。(该 Cause 是导致抛出此 throwable 的throwable。)
* 此方法至多可以调用一次。此方法通常从构造方法中调用,或者在创建 throwable 后立即调用。
* 如果此 throwable 通过 Throwable(Throwable) 或 Throwable(String,Throwable) 创建,此方法不能调用。
*/
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;
}
public String toString() {
String s = getClass().getName();
String message = getLocalizedMessage();
return (message != null) ? (s + ": " + message) : s;
}
public void printStackTrace() {
printStackTrace(System.err);
}
public void printStackTrace(PrintStream s) {
printStackTrace(new WrappedPrintStream(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);
}
}
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);
}
}
/**
*
* @param s {@code PrintWriter} to use for output
* @since JDK1.1
*/
public void printStackTrace(PrintWriter s) {
printStackTrace(new WrappedPrintWriter(s));
}
/**
*用于PrintStream和PrintWriter的WrapperClass(包装器类),以启用printStackTrace的单个实现
*/
private abstract static class PrintStreamOrWriter {
/** Returns the object to be locked when using this StreamOrWriter */
abstract Object lock();
/** Prints the specified string as a line on this StreamOrWriter */
abstract void println(Object o);
}
private static class WrappedPrintStream extends PrintStreamOrWriter {
private final PrintStream printStream;
WrappedPrintStream(PrintStream printStream) {
this.printStream = printStream;
}
Object lock() {
return printStream;
}
void println(Object o) {
printStream.println(o);
}
}
private static class WrappedPrintWriter extends PrintStreamOrWriter {
private final PrintWriter printWriter;
WrappedPrintWriter(PrintWriter printWriter) {
this.printWriter = printWriter;
}
Object lock() {
return printWriter;
}
void println(Object o) {
printWriter.println(o);
}
}
/*
*填充 执行栈跟踪。这个方法在此抛出对象信息
*这个方在Throwable Object信息中记录着关于当前线程栈结构的当前状态
*如果这个Throwable的the Stack trace不是可写的,那么调用这个这个方法没有任何效果
*/
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
stackTrace = UNASSIGNED_STACK;
}
return this;
}
//这里修饰了一个原生方法
private native Throwable fillInStackTrace(int dummy);
/**
* 提供编程访问由 printStackTrace() 输出的堆栈跟踪信息。
* 返回堆栈跟踪元素的数组,每个元素表示一个堆栈帧。
* 数组的第零个元素(假定数据的长度为非零)表示堆栈顶部,它是序列中最后的方法调用。
*/
public StackTraceElement[] getStackTrace() {
return getOurStackTrace().clone();
}
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;
}
/**
* 设置将由 getStackTrace() 返回,并由 printStackTrace() 和相关方法输出的堆栈跟踪元素。
* 此方法设计用于 RPC 框架和其他高级系统,允许客户端重写默认堆栈跟踪
* /
public void setStackTrace(StackTraceElement[] stackTrace) {
// Validate argument
StackTraceElement[] defensiveCopy = stackTrace.clone();
for (int i = 0; i < defensiveCopy.length; i++) {
if (defensiveCopy[i] == null)
throw new NullPointerException("stackTrace[" + i + "]");
}
synchronized (this) {
if (this.stackTrace == null && // Immutable stack
backtrace == null) // Test for out of protocol state
return;
this.stackTrace = defensiveCopy;
}
}
/**
* 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);
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject(); // read in all fields
if (suppressedExceptions != null) {
List<Throwable> suppressed = null;
if (suppressedExceptions.isEmpty()) {
// Use the sentinel for a zero-length list
suppressed = SUPPRESSED_SENTINEL;
} else { // Copy Throwables to new list
suppressed = new ArrayList<>(1);
for (Throwable t : suppressedExceptions) {
// Enforce constraints on suppressed exceptions in
// case of corrupt or malicious stream.
if (t == null)
throw new NullPointerException(NULL_CAUSE_MESSAGE);
if (t == this)
throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE);
suppressed.add(t);
}
}
suppressedExceptions = suppressed;
} // else a null suppressedExceptions field remains null
if (stackTrace != null) {
if (stackTrace.length == 0) {
stackTrace = UNASSIGNED_STACK.clone();
} else if (stackTrace.length == 1 &&
// Check for the marker of an immutable stack trace
SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL.equals(stackTrace[0])) {
stackTrace = null;
} else { // Verify stack trace elements are non-null.
for(StackTraceElement ste : stackTrace) {
if (ste == null)
throw new NullPointerException("null StackTraceElement in serial stream. ");
}
}
} else {
// A null stackTrace field in the serial form can result
// from an exception serialized without that field in
// older JDK releases; treat such exceptions as having
// empty stack traces.
stackTrace = UNASSIGNED_STACK.clone();
}
}
/**
*需要将对象进行序列化操作才能输出:
*在Throwable类中使用输出流来进行输出,并把其对象作为输出流对象,
*这就需要将对象进行序列化操作才能输出
*/
private synchronized void writeObject(ObjectOutputStream s)
throws IOException {
// 确保stackTrace字段初始化为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;
}
}
/**
*
* @param exception the exception to be added to the list of
* suppressed exceptions
* @throws IllegalArgumentException if {@code exception} is this
* throwable; a throwable cannot suppress itself.
* @throws NullPointerException if {@code exception} is {@code null}
* @since 1.7
*/
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);
}
private static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0];
/**
*getSuppressed方法返回了被抑制(可能是try住的)的所有的异常信息
*/
public final synchronized Throwable[] getSuppressed() {
if (suppressedExceptions == SUPPRESSED_SENTINEL ||
suppressedExceptions == null)
return EMPTY_THROWABLE_ARRAY;
else
return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY);
}
}
#Throwable.fillInStackTrace()
这次源码追踪从最顶层的子类ArithmeticException到RuntimeException再到Exception最后到Throwable所有Errors和Exceptions的superclass,追到Throwable后观察其的所有构造函数,所有的构造函数中,首先第一件事是去填充栈追踪fillInStackTrace(),查看其实现,没料到居然在方法体里调用
private native Throwable fillInStackTrace(int dummy);
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
stackTrace = UNASSIGNED_STACK;
}
return this;
}
好吧,底层采用的是JNI(Java Native Interface),是通过其它语言(C/C++)来实现的,这个迟点又可以续一波博客了。。。
这里我们探究一下fillInStackTrace()的用法,上个实例先
package Exception;
public class ThrowsException {
Throwable th = new Throwable();
public ThrowsException() {
System.out.println("ThrowsException无参构造函数");
}
public void object_1() {
object_3();
}
public void object_2() {
System.out.println("2。。。。start");
//System.out.println(10/0);
th.fillInStackTrace();
th.printStackTrace(System.out);
System.out.println("2。。。。end");
}
public void object_3() {
object_2();
th.fillInStackTrace();
System.out.println("3。。。start");
th.printStackTrace(System.out);
System.out.println("3。。。end");
}
public static void main(String[] args) {
// Error error = new Error("Throws a error message");
// System.err.println(error.toString());
new ThrowsException().object_1();
}
}
显示结果:
ThrowsException无参构造函数
2。。。。start
java.lang.Throwable
at Exception.ThrowsException.object_2(ThrowsException.java:17)
at Exception.ThrowsException.object_3(ThrowsException.java:23)
at Exception.ThrowsException.object_1(ThrowsException.java:11)
at Exception.ThrowsException.main(ThrowsException.java:35)
2。。。。end
3。。。start
java.lang.Throwable
at Exception.ThrowsException.object_3(ThrowsException.java:24)
at Exception.ThrowsException.object_1(ThrowsException.java:11)
at Exception.ThrowsException.main(ThrowsException.java:35)
3。。。end
再把object_2()中的//System.out.println(10/0);注释去掉
public void object_2() {
System.out.println("2。。。。start");
//System.out.println(10/0);
th.fillInStackTrace();
th.printStackTrace(System.out);
System.out.println("2。。。。end");
}
显示结果
ThrowsException无参构造函数
Exception in thread "main" 2。。。。start
java.lang.ArithmeticException: / by zero
at Exception.ThrowsException.object_2(ThrowsException.java:16)
at Exception.ThrowsException.object_3(ThrowsException.java:23)
at Exception.ThrowsException.object_1(ThrowsException.java:11)
at Exception.ThrowsException.main(ThrowsException.java:35)
结论:fillInStackTrace()每次执行的时候都会清空栈内的trace信息,然后在当前调用的位置(抛出异常位置)重新建立trace
#finally的使用
一、
try {
System.exit(0);
} catch (Exception e) {
e.printStackTrace();
}finally{
System.out.println("触发finally");
}
上面片段想要表达的是finally也有不执行的时候,那就是JVM被退出了
二、
try(Resource res = ...)
{
//do some work
}
带资源的try块,在javase7中新增的方法,在这种写法下,try快退出时res会自动调用res.close(),这种写法仅仅在是“close”方法的时候可用。
三、finally导致的异常丢失问题
public static void main(String[] args) {
try {
throw new RuntimeException();
} finally{
// return;
int i = 1;
int j = 0;
int k = i/j;
}
}
try块中捕获到了异常,然后在finally中也捕获到了异常,try块的异常会被覆盖,这里是Java异常设计的缺陷,但是当你在finally返回 "return;"时发现控制台什么输出的信息都没有,亿脸懵逼,因此建议在设计程序的时候尽量少把逻辑性代码放进finally内
#异常处理的两种方式
https://blog.youkuaiyun.com/dengminghli/article/details/71598636