一、异常简介
优秀的程序代码,都在追求高效,安全,和低错误率,但是程序中的异常是无法避免的,降低异常出现的频率是关键,异常出现如何处理是另一个重要方面,Java体系中异常框架对于系统开发是十分重要的。
面对系统异常时,不要慌乱,异常虽然是错误,也是系统发出的消息,标识系统的缺陷和需要改进的地方。
二、API体系
Java的API中已经定义许多异常类,分为两大类,错误Error和异常Exception,Throwable作为所有异常的超类,如图:
Error:一般为底层的不可恢复的类,一般此类错误都比较严重,JVM将终止其运行的线程;
- VirtualMachineError:虚拟机运行错误;
- OutOfMemoryError:内存溢出;
Exception:程序本身可以捕获并且可以预处理的异常,例如捕获或者抛出;
- RuntimeException:运行时异常;
- CheckException:已检查异常,编译阶段必须处理;
几个经典的常见的RunTimeException如下:空指针NullPointerException;数组下标越界ArrayIndexoutofBoundsException等。
三、异常处理
Java异常处理关键字,分别是:try、catch、finally、throw、throws。
应该在合适的位置处理异常,异常的处理准则如下:谁知情谁处理,谁负责谁处理,谁导致谁处理。
1、抛出异常
即异常在当前流程下不处理,一种是直接通过方法传递给调用者,throws关键字是用于在方法声明上声明抛出异常类型的,并且一次可以声明抛出多种类型的异常。throw关键字是用于方法的内部抛出一个异常对象,常在业务校验时抛出提示。
需要特别说明的一点,在Spring框架中,事务触发多数是以是否抛出异常为标识来处理的,如果方法在事务控制内,方法内异常捕获但是最终没有抛出,那该事务则无效。
2、捕获异常
通常捕获异常会使用try-catch-finally关键字三连操作:
Try尝试捕获异常:
如果语句依次执行结束,则跳过catch,在存在finally代码块时,则执行否则执行后续流程;
如果捕获异常,则匹配catch中的类型,如果没有与之匹配的catch类型,则该异常交给JVM处理,finally代码会被执行,流程之后的代码不会被执行;
如果捕获异常且存在相匹配的catch类型,则跳到catch代码块执行,finally代码会被执行,执行完finally代码块之后继续执行后续代码;
Catch匹配可能出现的异常类型,并在其中做补偿处理,例如出现异常情况,需要更新一个异常状态等,如果没有catch块,后必须跟finally块,处理资源释放;
Finally无论是否捕获异常,finally代码会被执行,也是面试中常见的异常问题之一,例如在finally代码块return,或者修改返回值等,主要涉及到值传递和引用传递方面。
3、异常日志
复杂的业务系统必备功能,异常日志体系,用来分析运行问题,作为系统不断优化的核心依据,通常会记录如下几块:
- 异常类型:分析异常发生的关键原因;
- 异常信息:通常会简单记录e.getMsg输出的内容;
- 异常位置:快速定位异常发生的位置[类.方法];
- 业务参数:特定业务参数场景才能复现的问题;
- 时间节点:有的并发问题是在特定时间段出现;
异常日志记录下来之后,还会定期进行任务分析,不断发现系统容易出问题的地方,然后再不断的改进和优化。
4、熔断降级
在微服务架构系统下,某个服务故障或者异常,触发熔断该服务,避免引发整个微服务链路异常,防止整个系统服务的雪崩。以此缓解服务器资源的的压力,以保证核心业务的正常运行。