为什么要有异常
一个优秀的程序应该有一个标志性的特点,就是他的异常处理相关代码应该要多余实际的业务代码,那这是为什么呢,这个问题有个比较笼统的回答,为了提升程序的健壮性。说白了,我要尽量去掌控自己程序中的异常情况,并且当异常发生时要准确的知道是哪里产生的问题。
java的异常机制是如何做到的呢
我们在程序出现异常的时候都可以看到哪些信息呢,首先调用栈信息,可以看到抛出异常的方法行数,同时可以打印caused by信息,找到异常发生的原因,这些信息是如何打印的呢,计算机没有无缘无故的爱,一定有地方提供了栈信息,并且提供了打印的方法。这里就要说一下java异常处理的祖宗类Throwable,上面的所有功能都是这个类提供的,要想了解java的异常机制,那么这个类是需要弄清楚的。这里先带着大家一起入个门。一言不合先上样例吧,这样看着能更明白点。
import myexception.MyExcepition;
public class CatchMain {
public static int b(){
c();
return 1;
}
public static int c(){
try{
d();
return 1;
}catch (MyExcepition ex){
throw new MyExcepition("这是c打印的错误信息!",ex);
}
}
public static int d(){
throw new MyExcepition("这是d打印的错误信息!");
}
public static void main(String[] args) {
try{
b();
}catch (MyExcepition ex){
ex.printStackTrace();
}
}
}
D:\workspace\study\DesignPattern\out\production\DesignPattern;D:\tools\lombok.jar;D:\tools\jaxb-api-2.0.jar;D:\.m2\repository\mysql\mysql-connector-java\8.0.11\mysql-connector-java-8.0.11.jar CatchMain
myexception.MyExcepition: 这是c打印的错误信息!
at CatchMain.c(CatchMain.java:14)
at CatchMain.b(CatchMain.java:6)
at CatchMain.main(CatchMain.java:24)
Caused by: myexception.MyExcepition: 这是d打印的错误信息!
at CatchMain.d(CatchMain.java:19)
at CatchMain.c(CatchMain.java:11)
... 2 more
Process finished with exit code 0
package myexception;
public class MyExcepition extends RuntimeException {
private String msg;
public MyExcepition(String msg){
super(msg);
this.msg = msg;
}
public MyExcepition(String msg,Throwable e){
super(msg,e);
this.msg = msg;
}
}
通过上面的代码,可以先看下自定义 MyExcepition 的继承关系,MyException -> RuntimeException -> Exception -> Throwable ,所有的自定义异常一定要是Throwable的子类,否则 throw new MyException()是无法编译通过的。另外如果细心的读者可能会发现,Exception和RuntimeException这两个子类没有扩展什么方法,功能跟Throwable提供的功能完全一致,那么为什么要多此一举呢,搞这么多子类干啥,这里留一个悬念,大家也想一下。
还有一个问题,大家看下实际上代码中抛出异常的是方法d(),可以栈信息只到了方法c(),栈的链路不完整,这个是为啥,原因在于我在方法c()中进行了一次catch,同时又抛出了一个新的异常,这个异常是覆盖掉了d()方法的异常,这里有个就近原则,可是在caused by中却把d()中的异常信息打印出来了,勾勒出了c和d的异常信息,这个又是因为什么呢,原因在于throwable的一个构造函数,
public MyExcepition(String msg,Throwable e){
super(msg,e);
this.msg = msg;
}
public Throwable(String message, Throwable cause) {
fillInStackTrace();
detailMessage = message;
this.cause = cause;
}
用代码直接对比,可以看到detailMessage,其实就是“这是c打印的错误信息!”而这个this.cause就是
Caused by: myexception.MyExcepition: 这是d打印的错误信息!
at CatchMain.d(CatchMain.java:19)
at CatchMain.c(CatchMain.java:11)
… 2 more
在构造函数中 super(msg,e); 给this.cause赋值,从而拥有了caused by打印的结果,这里的语义就是告诉jvm,我抛出异常了,但是根源不是我错了,而是e。
写着写着感觉有点不易理解,可以多看两遍,掌握这个原理后就可以随心所欲的定义自定义异常,并且可以控制控制台打印的日志内容。