错误和异常是指程序在编译和运行时出现的问题。
Error是程序无法处理的错误,比如OutOfMemoryError、ThreadDeath等。这些异常发生时, Java虚拟机(JVM)一般会选择线程终止。
Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。程序中应当尽可能去处理这些异常。在程序中对于异常的处理是一个和有意思的过程,一般而言有错误或者异常,在其发生的地方处理就可以了,没有什么可以进一步讨论的。但是,如果异常很多的话,你加了很多的讨try…catch….finally语句,自然导致了程序可读性的降低,不便于后期的维护,而你如果向外抛异常的话,那么什么时候处理这个异常比较好,是个系统性考虑的过程。对与错误及异常转化的方向大致有三个方向:
错误→异常:这里对于可能会使系统因错误而挂起或者崩溃的错误来说,可以使得系统依然能被运行,这就提高了系统的健壮性,可以处理更多的信息。
异常→运行时异常。对于有的异常我们如果不知道怎么处理,可以将它进一步向外抛,直到系统在运行期间,由于错误的输入导致被阻塞,再进行处理。
错误→运行时异常。这可以让系统被执行,直到运行时才被发现和处理。
在java中,错误(Error)和异常(Exception)都继承自Throwable类。对于Throwable类,它的主要作用是把错误信息压入栈然后根据异常或者错误发生的先后依次打印异常或者错误信息。下面重点剖析一下源码。
public class Throwable implements Serializable {
private transient Object backtrace; //不能被序列化的属性
• private String detailMessage; //记录异常信息
• private Throwable cause = this; //导致异常的对象
• private StackTraceElement[] stackTrace; //描述异常发生顺序及信息的数组
• 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) { //构造方法的参数是一个Throwable类型的对象。
• fillInStackTrace();
• detailMessage = (cause==null ? null : cause.toString());
• this.cause = cause;
• }
• public String getMessage() {
• return detailMessage; //获取错误信息
• }
• public String getLocalizedMessage() { //调用上一个方法,等价与执行的是上一个方法。
• return getMessage();
• }
• //获取导致异常的对象
• public Throwable getCause() {
• return (cause==this ? null : cause);
• }
•
• /**
• * 初始化起因对象,这个方法只能在未被初始化的情况下调用一次
• */
• public synchronized Throwable initCause(Throwable cause) {
• //如果不是未初始化状态则抛出异常
• if (this.cause != this)
• throw new IllegalStateException("Can't overwrite cause");
•
• //要设置的起因对象与自身相等则抛出异常
• if (cause == this)
• throw new IllegalArgumentException("Self-causation not permitted");
•
• //设置起因对象
• 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);
• }
•
• /**
• * 打印出错误(异常)追踪栈的方法(异步的,由synchronized修饰,线程安全的)
• */
• public void printStackTrace(PrintStream s) {
• synchronized (s) { //锁的是传入的打印流对象
• //调用当前对象的toString方法
• s.println(this);
• //获取异常轨迹数组
• StackTraceElement[] trace = getOurStackTrace();
• //打印出每个元素的字符串表示
• for (int i=0; i < trace.length; i++)
• s.println("\tat " + trace[i]);
• //获取起因对象
• Throwable ourCause = getCause();
• //递归的打印出起因对象的信息 (也就是在查错误时最先打印的是因出错最后无法处理的地方,所以,先查最后一个错误输出,可以找到最先出错的地方
。)
• if (ourCause != null)
• ourCause.printStackTraceAsCause(s, trace);
• }
• }
•
• /**
• * 打印起因对象的信息
• * @param s 打印的流
• * @param causedTrace 有此对象引起的异常的异常轨迹
• */
• private void printStackTraceAsCause(PrintStream s,
• StackTraceElement[] causedTrace)
• {
• //获得当前的异常轨迹
• StackTraceElement[] trace = getOurStackTrace();
• //m为当前异常轨迹数组的最后一个元素位置,
• //n为当前对象引起的异常的异常轨迹数组的最后一个元素
• int m = trace.length-1, n = causedTrace.length-1;
• //分别从两个数组的后面做循环,如果相等则一直循环,直到不等或数组到头
• while (m >= 0 && n >=0 && trace[m].equals(causedTrace[n])) {
• m--; n--;
• }
•
• //相同的个数
• int framesInCommon = trace.length - 1 - m;
•
• //打印出不同的错误轨迹
• s.println("Caused by: " + this);
• for (int i=0; i <= m; i++)
• s.println("\tat " + trace[i]);
• //如果有相同的则打印出相同的个数
• if (framesInCommon != 0)
• s.println("\t... " + framesInCommon + " more");
•
• //获得此对象的起因对象,并递归打印出信息
• Throwable ourCause = getCause();
• if (ourCause != null)
• ourCause.printStackTraceAsCause(s, trace);
• }
• /**
• * 打印导致错误的对象的信息
• */
• private void printStackTraceAsCause(PrintWriter s,
• StackTraceElement[] causedTrace)
• {
• // assert Thread.holdsLock(s);
• // Compute number of frames in common between this and caused
//计算当前对象和导致错误对象之间的相同结构(框架)
• StackTraceElement[] trace = getOurStackTrace();
• int m = trace.length-1, n = causedTrace.length-1; //m是当前对象栈的长度,n是导致错误对象栈的长度
• while (m >= 0 && n >=0 && trace[m].equals(causedTrace[n])) {
• m--; n--;
• }
• int framesInCommon = trace.length - 1 - m;
•
• s.println("Caused by: " + this); //找到两个栈最大的公共长度。
• for (int i=0; i <= m; i++)
• s.println("\tat " + trace[i]);
• if (framesInCommon != 0)
• s.println("\t... " + framesInCommon + " more");
• // Recurse if we have a cause
• Throwable ourCause = getCause();
• if (ourCause != null)
• ourCause.printStackTraceAsCause(s, trace);
• }
•
• /**
• * 填充异常轨迹
• */
• public synchronized native Throwable fillInStackTrace();
• //返回异常追踪栈对象的一个克隆对象。
• public StackTraceElement[] getStackTrace() {
• return (StackTraceElement[]) getOurStackTrace().clone();
• }
• //获取当前的异常轨迹 并存放在数组中,元素是每个错误对象
• private synchronized StackTraceElement[] getOurStackTrace() {
• //如果第一次调用此方法则初始化异常轨迹数组
• if (stackTrace == null) {
• //获得异常栈深度
• int depth = getStackTraceDepth();
• //创建新的异常轨迹数组,并填充它
• stackTrace = new StackTraceElement[depth];
• for (int i=0; i < depth; i++)
• stackTrace[i] = getStackTraceElement(i);//获取指定位标的异常轨迹
• }
• return stackTrace;
• }
• /**
* 设置异常轨迹
*/
public void setStackTrace(StackTraceElement[] stackTrace) {
//拷贝设置参数
StackTraceElement[] defensiveCopy =
(StackTraceElement[]) stackTrace.clone();
//如果设置参数有空元素则抛出异常
for (int i = 0; i < defensiveCopy.length; i++)
if (defensiveCopy[i] == null)
throw new NullPointerException("stackTrace[" + i + "]");
//设置当前对象的异常轨迹
this.stackTrace = defensiveCopy;
}
}
Error是程序无法处理的错误,比如OutOfMemoryError、ThreadDeath等。这些异常发生时, Java虚拟机(JVM)一般会选择线程终止。
Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。程序中应当尽可能去处理这些异常。在程序中对于异常的处理是一个和有意思的过程,一般而言有错误或者异常,在其发生的地方处理就可以了,没有什么可以进一步讨论的。但是,如果异常很多的话,你加了很多的讨try…catch….finally语句,自然导致了程序可读性的降低,不便于后期的维护,而你如果向外抛异常的话,那么什么时候处理这个异常比较好,是个系统性考虑的过程。对与错误及异常转化的方向大致有三个方向:
错误→异常:这里对于可能会使系统因错误而挂起或者崩溃的错误来说,可以使得系统依然能被运行,这就提高了系统的健壮性,可以处理更多的信息。
异常→运行时异常。对于有的异常我们如果不知道怎么处理,可以将它进一步向外抛,直到系统在运行期间,由于错误的输入导致被阻塞,再进行处理。
错误→运行时异常。这可以让系统被执行,直到运行时才被发现和处理。
在java中,错误(Error)和异常(Exception)都继承自Throwable类。对于Throwable类,它的主要作用是把错误信息压入栈然后根据异常或者错误发生的先后依次打印异常或者错误信息。下面重点剖析一下源码。
public class Throwable implements Serializable {
private transient Object backtrace; //不能被序列化的属性
• private String detailMessage; //记录异常信息
• private Throwable cause = this; //导致异常的对象
• private StackTraceElement[] stackTrace; //描述异常发生顺序及信息的数组
• 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) { //构造方法的参数是一个Throwable类型的对象。
• fillInStackTrace();
• detailMessage = (cause==null ? null : cause.toString());
• this.cause = cause;
• }
• public String getMessage() {
• return detailMessage; //获取错误信息
• }
• public String getLocalizedMessage() { //调用上一个方法,等价与执行的是上一个方法。
• return getMessage();
• }
• //获取导致异常的对象
• public Throwable getCause() {
• return (cause==this ? null : cause);
• }
•
• /**
• * 初始化起因对象,这个方法只能在未被初始化的情况下调用一次
• */
• public synchronized Throwable initCause(Throwable cause) {
• //如果不是未初始化状态则抛出异常
• if (this.cause != this)
• throw new IllegalStateException("Can't overwrite cause");
•
• //要设置的起因对象与自身相等则抛出异常
• if (cause == this)
• throw new IllegalArgumentException("Self-causation not permitted");
•
• //设置起因对象
• 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);
• }
•
• /**
• * 打印出错误(异常)追踪栈的方法(异步的,由synchronized修饰,线程安全的)
• */
• public void printStackTrace(PrintStream s) {
• synchronized (s) { //锁的是传入的打印流对象
• //调用当前对象的toString方法
• s.println(this);
• //获取异常轨迹数组
• StackTraceElement[] trace = getOurStackTrace();
• //打印出每个元素的字符串表示
• for (int i=0; i < trace.length; i++)
• s.println("\tat " + trace[i]);
• //获取起因对象
• Throwable ourCause = getCause();
• //递归的打印出起因对象的信息 (也就是在查错误时最先打印的是因出错最后无法处理的地方,所以,先查最后一个错误输出,可以找到最先出错的地方
。)
• if (ourCause != null)
• ourCause.printStackTraceAsCause(s, trace);
• }
• }
•
• /**
• * 打印起因对象的信息
• * @param s 打印的流
• * @param causedTrace 有此对象引起的异常的异常轨迹
• */
• private void printStackTraceAsCause(PrintStream s,
• StackTraceElement[] causedTrace)
• {
• //获得当前的异常轨迹
• StackTraceElement[] trace = getOurStackTrace();
• //m为当前异常轨迹数组的最后一个元素位置,
• //n为当前对象引起的异常的异常轨迹数组的最后一个元素
• int m = trace.length-1, n = causedTrace.length-1;
• //分别从两个数组的后面做循环,如果相等则一直循环,直到不等或数组到头
• while (m >= 0 && n >=0 && trace[m].equals(causedTrace[n])) {
• m--; n--;
• }
•
• //相同的个数
• int framesInCommon = trace.length - 1 - m;
•
• //打印出不同的错误轨迹
• s.println("Caused by: " + this);
• for (int i=0; i <= m; i++)
• s.println("\tat " + trace[i]);
• //如果有相同的则打印出相同的个数
• if (framesInCommon != 0)
• s.println("\t... " + framesInCommon + " more");
•
• //获得此对象的起因对象,并递归打印出信息
• Throwable ourCause = getCause();
• if (ourCause != null)
• ourCause.printStackTraceAsCause(s, trace);
• }
• /**
• * 打印导致错误的对象的信息
• */
• private void printStackTraceAsCause(PrintWriter s,
• StackTraceElement[] causedTrace)
• {
• // assert Thread.holdsLock(s);
• // Compute number of frames in common between this and caused
//计算当前对象和导致错误对象之间的相同结构(框架)
• StackTraceElement[] trace = getOurStackTrace();
• int m = trace.length-1, n = causedTrace.length-1; //m是当前对象栈的长度,n是导致错误对象栈的长度
• while (m >= 0 && n >=0 && trace[m].equals(causedTrace[n])) {
• m--; n--;
• }
• int framesInCommon = trace.length - 1 - m;
•
• s.println("Caused by: " + this); //找到两个栈最大的公共长度。
• for (int i=0; i <= m; i++)
• s.println("\tat " + trace[i]);
• if (framesInCommon != 0)
• s.println("\t... " + framesInCommon + " more");
• // Recurse if we have a cause
• Throwable ourCause = getCause();
• if (ourCause != null)
• ourCause.printStackTraceAsCause(s, trace);
• }
•
• /**
• * 填充异常轨迹
• */
• public synchronized native Throwable fillInStackTrace();
• //返回异常追踪栈对象的一个克隆对象。
• public StackTraceElement[] getStackTrace() {
• return (StackTraceElement[]) getOurStackTrace().clone();
• }
• //获取当前的异常轨迹 并存放在数组中,元素是每个错误对象
• private synchronized StackTraceElement[] getOurStackTrace() {
• //如果第一次调用此方法则初始化异常轨迹数组
• if (stackTrace == null) {
• //获得异常栈深度
• int depth = getStackTraceDepth();
• //创建新的异常轨迹数组,并填充它
• stackTrace = new StackTraceElement[depth];
• for (int i=0; i < depth; i++)
• stackTrace[i] = getStackTraceElement(i);//获取指定位标的异常轨迹
• }
• return stackTrace;
• }
• /**
* 设置异常轨迹
*/
public void setStackTrace(StackTraceElement[] stackTrace) {
//拷贝设置参数
StackTraceElement[] defensiveCopy =
(StackTraceElement[]) stackTrace.clone();
//如果设置参数有空元素则抛出异常
for (int i = 0; i < defensiveCopy.length; i++)
if (defensiveCopy[i] == null)
throw new NullPointerException("stackTrace[" + i + "]");
//设置当前对象的异常轨迹
this.stackTrace = defensiveCopy;
}
}