一、异常的体系结构
- Throwable:是java中所有异常和错误的父类,其包括两个子类Error(错误)和Exception(异常)。
- Error:是程序中无法处理的错误,一般是指与虚拟机相关的问题,如系统崩溃、虚拟机错误、动态链接失败等。这类错误无法恢复或捕获,将会导致应用程秀中断。此类异常发生时,必须手动解决。
- Exception:是程序可以捕获并可以处理的异常。主要分为运行时异常(不受检异常)和受检异常。
- RuntimeException:是运行时异常,也是不受检异常。此类异常一般是由程序逻辑错误引起的,为编译器不要求强制处理的异常,可以选择处理解决,也可以不处理。
- 非运行时异常(受检异常):指Exception中除RuntimeException及其子类之外的异常。编译器会检查此类异常,若检查出来则必须解决处理,要么用try-catch捕获处理,要么用throw将异常交给方法的调用者处理,否则编译不通过。
二、异常的处理方法
2.1 try - catch - finally处理
- try:通常在try{}中放可能出现异常的业务逻辑代码。如果try代码块中发生异常,则会生成一个异常类的对象,会与catch()中的异常类进行匹配,匹配成功就会被catch捕获然后运行catch中的代码;匹配不成功则程序终止。
- catch:通常用来捕获try{}中发生的异常,并放置异常处理代码。如果处理完成后,没有finally{}则退出try-catch结构;存在finally{},则去执行finally{}中的代码。
- catch多个异常类型的时候, 会从上到下进行捕获,上面捕获成功后下面的不会再捕获。如果有子父类关系,小的范围写上面而大的范围写下面; 如果没有子父类关系,谁在上谁在下无所谓。
- 可以在catch块中访问异常对象的相关信息,通过访问catch块的后异常形参的相应方法来获得
getMessage():返回该异常信息的跟踪栈信息输出到标准错误输出
printStackTrace():将该异常的跟踪栈信息输出到标准错误输出。
printStackTrace(PrintStream s):将该异常的跟踪栈信息输出到指定输出流。
getStackTrace():返回该异常的跟踪栈信息。
- finally:finally{}一般放置资源回收的代码,用于回收在try块里打开的物理资源(如数据库连接、网络连接和磁盘文件)。不管try块中的代码是否出现异常,也不管哪一个catch块被执行,甚至在try块或catch块中执行了return语句,finally块总会被执行。
- 只有finally块执行完成之后,才会回来执行try或者catch块中的return或者throw语句。如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
try{
//业务实现代码
...
}catch(SubException e){
//异常处理块
...
}catch(SubException e2){
//异常处理块
...
}finally{
//资源回收
...
}
2.2 throws + 异常类型
throws 一般用于当前方法不知道如何处理这种类型的异常,该异常应该由上一级调用者处理。如果方法的上一级无法解决就会再将异常向上抛出, 最终会抛给main方法。这样一来main方法中调用了这个方法的时候,就需要解决这个可能出现的异常。当然main方法也可以不解决异常, 将异常往上抛出给JVM,JVM对异常的处理方法是,打印异常的跟踪栈信息,并中止程序运行。如果java虚拟机也无法解决的话,那么JVM就over了。
注意:子类重写的方法抛出的异常类型不能大于父类被重写的方法抛出的异常类型,否则会catch不到子类重写的方法抛出的异常。
2.3 合理使用try - catch - finally和throws + 异常类型
- 如果父类中被重写的方法没有用throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用tyr-catch-finally方式处理。
- 执行的方法中,先后又调用了另外的几个方法,这几个方法是递进关系执行的,建议这几个方法使用throws的方式处理,而执行的方法使用try-catch-finally方式进行处理。
三、查看异常堆栈信息
public class Test {
public static void fun1() {
fun2();
}
public static void fun2() {
fun3();
}
public static void fun3() {
try {
fun4();
} catch (Exception e) {
throw new RuntimeException("fun3",e);
}
}
public static void fun4() {
throw new RuntimeException("fun4");
}
public static void main(String[] args) {
fun1();
}
}
既然是异常堆栈,那肯定是FILO(先进后出)的。调用顺序为main->fun1->fun2->fun3->fun4。第一部分的java.lang.RuntimeException是在fun3中抛出的,所以第一部分异常堆栈信息为fun3->fun2->fun1->main。
又因为但是fun3中抛出的异常中传入了一个cause,用于设置抛出该异常的原因。所以就有了第二部分的Caused by,这个打印的是fun3中捕获的异常的栈信息。而fun4中抛出的异常的栈信息前半部分与第一部分异常栈重合,所以printStackTrace()方法省略了这部分的打印。
异常堆栈信息总结
- 异常栈信息的第一行就是抛出这个异常的最原始的位置,最后一行就是最开始调用的地方。
- 如果异常栈信息后面跟着Cause by,就证明抛出当前异常的原因是捕获到了下面的异常。
- 快速分析异常信息,首先查看异常栈信息最开始的那一行了解异常的类型,再找到最开始调用的位置进行定位,结合Cause by分析发生异常的根本原因,最后在定位的位置进行调试修改。
参考资料:https://www.cnblogs.com/brokyz/p/14672500.html#42-throws--%E5%BC%82%E5%B8%B8%E7%B1%BB%E5%9E%8B
https://www.cnblogs.com/wugongzi/p/11858228.html
https://blog.csdn.net/tianjindong0804/article/details/105117779