目录
一,关于Throwable,Exception,Error
Throwable类
Error类
Exception类
关于cause
二,Throwable类中的方法和说明
1,fillInStackTrace()
2,getCause()
3,initCause()
4,getMessage()
5,getLocalizedMessage()
6,printStackTrace()
7,getStackTrace()
一,关于Throwable,Exception,Error
要介绍Exception,首先要看看Exception在Java语言中的位置,下图是Exception的大家庭:
可以看到,Java语言中所有错误和异常的超类是Throwable类,他有两个子类:Error类(异常)和Exception类(错误),Error类和Exception类各自还有无穷无尽的子类和后代。
Throwable类
Throwable类是Java语言中所有错误和异常的超类,异常和错误中的很多方法都是从该类继承来的,比如最常用的e.printStackTrace什么的,这个类有以下两个主要特点:
1,可抛。正如类名所示,Throwable类的实例,或者其子类、后代的实例,可以在throw语句中向上层抛出,就像这样:
throw new Throwable();
2,可接。能抛出自然能接到,Throwable类的实例,或者其子类、后代的实例,可以在catch语句中捕获。
另外,从JDK1.7开始,一行catch语句可以包含多个Throwable类型了。
Error类
Error类代表错误,代表Java程序运行时发生的严重问题,通常情况下出现Error时连catch都没有必要。在Error的子类中,ThreadDeath类的情节相对较轻,但也不建议在程序中捕获这个错误,具体说明可以参考Java的API文档。
Error类的子类有以下这些:
Exception类
主角登场,Exception类,他代表异常,代表Java程序在正常运行期间发生并意图捕获的场景(或者说条件)。
和Error类不同,Exception类代表了合理的、可控的、需要在代码中写明处理方法的场景。
Exception类并没有比他的父类Throwable多写多少方法。
Exception的子类就多了:
可以看到,我们平时常见的多数异常,比如空指针异常NullPointerException……并不在这个列表中,因为NullPointerException是RuntimeException的子类。
所以,如果你想在代码里抛点什么,可以使用Throwable类或Exception类(和Exception类的后代),或者干脆自定义一个类来继承他们。
Error类还是不要继承了,Java可能并不喜欢你这么做。
关于cause
Java的Throwable有cause的概念,看Java的API文档感觉有点晦涩难懂,研究了一下大概是这样的:
cause指的是引发Throwable的原因,这个原因也是一个Throwable实例,每个Throwable都可以有cause。
鉴于Throwable的cause也是一个Throwable实例,所以这个作为cause的Throwable实例也可以有cause,cause包含cause这件事一直延续下去,就有了异常链的概念。
cause概念的建立,一方面是为了包装底层的异常,另一方面是为了抛出自己想要的异常,而不是底层实际的那个异常。
想给一个Throwable实例指定cause,有两种方式:在构造中指定和在initCause(Throwable cause)方法中指定。
1,在构造中指定。
从Throwable类开始,就有这样的构造:
Throwable(String message, Throwable cause)
或
Throwable(Throwable cause)
Throwable类的子类们也有很多可以使用这样的构造,如果用这种方式创建Throwable实例,其中的cause参数就会成为实例的cause。
比如:
Throwable throwable=new Throwable(new NullPointerException());
System.out.println(throwable.getCause());
输出的结果是:
java.lang.NullPointerException
新建的NullPointerException实例就是throwable实例的cause。
2,在initCause(Throwable cause)方法中指定。
initCause(Throwable cause)方法是由Throwable类实现的,子类们也可以直接使用,比如:
Throwable throwable2=new Throwable();
throwable2.initCause(new NullPointerException());
System.out.println(throwable2.getCause());
输出的结果是:
java.lang.NullPointerException
二,Throwable类中的方法和说明
1,fillInStackTrace()
官方说明是:在异常堆栈跟踪中填充。
看不懂。
这个方法的实际功能是这样的:以调用该方法的代码位置作为调用栈的顶端,重新填充异常的调用栈信息。
意即:不管多底层抛出来的异常,我这调一下这个方法,我这就成调用栈顶端了,再printStackTrace的时候就看不见底层的调用栈了。
举个例子:
public static void main(String[] args) {
try {
Throwable th=testA();
th.fillInStackTrace();
th.printStackTrace();//此处打印第三个异常调用栈信息
} catch (Throwable e) {
e.printStackTrace();
}
}
public static Throwable testA() throws Throwable {
try {
testB();
} catch (Exception e) {
e.printStackTrace();//此处打印第一个异常调用栈信息
e=(Exception) e.fillInStackTrace();
e.printStackTrace();//此处打印第二个异常调用栈信息
return e;
}
return null;
}
public static void testB() throws Throwable{
throw new NullPointerException();
}
输出的结果是这样的:
java.lang.NullPointerException
at test.ExceptionTest2.testB(ExceptionTest2.java:37)
at test.ExceptionTest2.testA(ExceptionTest2.java:26)
at test.ExceptionTest2.main(ExceptionTest2.java:14)
java.lang.NullPointerException
at test.ExceptionTest2.testA(ExceptionTest2.java:29)
at test.ExceptionTest2.main(ExceptionTest2.java:14)
java.lang.NullPointerException
at test.ExceptionTest2.main(ExceptionTest2.java:15)
解析一下,异常从testB()方法开始,并把这个异常抛给testA()方法。
testA()方法的catch代码第一次打印了异常的原始信息:
java.lang.NullPointerException
at test.ExceptionTest2.testB(ExceptionTest2.java:37)
at test.ExceptionTest2.testA(ExceptionTest2.java:26)
at test.ExceptionTest2.main(ExceptionTest2.java:14)
然后在testA()方法的catch代码块中调用fillInStackTrace(),这样testA()方法就成了调用栈顶端,所以第二次打印的异常信息就是这样的:
java.lang.NullPointerException
at test.ExceptionTest2.testA(ExceptionTest2.java:29)
at test.ExceptionTest2.main(ExceptionTest2.java:14)
testA()方法把异常用返回值的形式返回给了main()方法,然后main()方法调用fillInStackTrace()方法,于是main()方法成了这个异常的调用栈的顶端,于是第三次打印的异常信息就是这样的:
java.lang.NullPointerException
at test.ExceptionTest2.main(ExceptionTest2.java:15)
还是那句话,哪儿调用哪儿就变成顶端,不论异常是throw抛出的,还是用返回值返回的。
另外,这个方法是有返回值的,返回类型是Throwable,其实返回的就是调用这个方法的Throwable本身。
然鹅,即使是一个Exception实例来调用这个方法,返回值也是个Throwable,如果你真打算这么写:
e=(Exception) e.fillInStackTrace();
那么强制类型转换是必要的。
其实我没怎么理解这个方法的返回值有什么意义,因为当实例e调用这个方法时,返回值就是实例e本身,所以我完全可以简单的使用:
e.fillInStackTrace();
代码来刷新实例e的调用栈,而不需要拿到返回值,类型转换,再赋值给e自己,可能Java的设计者有什么其他的想法吧。
2,getCause()
如上文所说,这个方法用于获得Throwable的cause,如果没有cause则返回null。
3,initCause()
如上文所说,这个方法用于给Throwable实例指定cause。
API文档中说,这个方法只能调用一次,而且如果已经在构造里指定了cause,甚至一次也不能调用。
其实意思是说,如果一个Throwable实例已经有了cause,再调用这个方法时会报错。
比如:
Throwable a=new Throwable(new NullPointerException());
a.initCause(new NullPointerException());
throw a;
会报错:
java.lang.IllegalStateException: Can't overwrite cause with java.lang.NullPointerException
at java.lang.Throwable.initCause(Throwable.java:456)
at test.ExceptionTest2.testB(ExceptionTest2.java:38)
at test.ExceptionTest2.testA(ExceptionTest2.java:26)
at test.ExceptionTest2.main(ExceptionTest2.java:14)
Caused by: java.lang.Throwable: java.lang.NullPointerException
at test.ExceptionTest2.testB(ExceptionTest2.java:37)
... 2 more
再比如:
Throwable a=new Throwable();
a.initCause(new NullPointerException());
a.initCause(new NullPointerException());
会报错:
java.lang.IllegalStateException: Can't overwrite cause with java.lang.NullPointerException
at java.lang.Throwable.initCause(Throwable.java:456)
at test.ExceptionTest2.testB(ExceptionTest2.java:39)
at test.ExceptionTest2.testA(ExceptionTest2.java:26)
at test.ExceptionTest2.main(ExceptionTest2.java:14)
Caused by: java.lang.Throwable
at test.ExceptionTest2.testB(ExceptionTest2.java:37)
... 2 more
这样的设定可能是有关于异常链的考虑。
4,getMessage()
该方法用于得到一个Throwable实例的详细消息字符串。
设定这个字符串可以用以下构造:
Throwable(String message)
或
Throwable(String message, Throwable cause)
设置了message参数,就可以用getMessage()方法得到。
5,getLocalizedMessage()
该方法默认情况下和getMessage()功能相同,不过可以在自定义的Throwable子类中重写,输出自己想要的内容。
6,printStackTrace()
该方法用于将Throwable实例的信息输出到错误输出流,同System.err。
输出的内容包括:
1,第一行,Throwable实例调用toString()后的结果。
2,由fillInStackTrace()记录的调用栈信息。
3,cause信息。
7,getStackTrace()
异常的调用栈信息会被放在一个数组中,数组的第一个元素是调用栈的顶端,代表最后的方法调用。
这个方法用于返回这个调用栈信息数组。返回值类型是:StackTraceElement[]。
比如这样的代码:
try {
testA();
} catch (Throwable e) {
e.printStackTrace();
System.out.println("========================");
StackTraceElement[] elements=e.getStackTrace();
for(int i=0;i<elements.length;i++){
StackTraceElement element=elements[i];
System.out.println(element);
}
}
输出是这样的:
java.lang.Throwable
at test.ExceptionTest.testB(ExceptionTest.java:26)
at test.ExceptionTest.testA(ExceptionTest.java:22)
at test.ExceptionTest.main(ExceptionTest.java:8)
========================
test.ExceptionTest.testB(ExceptionTest.java:26)
test.ExceptionTest.testA(ExceptionTest.java:22)
test.ExceptionTest.main(ExceptionTest.java:8)
以上