Java核心技术之Eexception和Error
Exception和Error都是继承了Throwable类,在Java中只有Throwable类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成部分。
Exception和Error体现了Java平台设计者对不同的异常情况的分类。Exception是程序正常运行过程中,可以预料的意外情况,并且可以被捕获并且进行相应的处理;Error是指在正常情况下,不大可能出现的情况,绝大部分的Error都会导致程序处于非正常的,不可恢复状态。既然是非正常情况,所以不便于也不需要进行捕获,常见的比如OOM异常。
Exception又分为运行时异常和编译时异常。编译时异常在写代码的过程中就会报错,需要强制进行显示的捕获处理,而运行时异常(如NullPointException)则是可以通过编码避免的逻辑错误,并且不需要强制捕获。
一. 理解Throwable,Exception和Error的设计与分类
二. 异常处理的实践
在日常编码中,我们需要掌握try-catch-finally块,通过throw和throws来抛出异常。
- 进行异常捕获时尽量不要捕获类似Exception这样的通用异常,而是应该捕获特定异常,对于特定的业务场景可以自定义相应的异常。
- 不要生吞异常,我们需要抛出或者通过日志的方式来让我们快速的定位异常。
- Java的异常处理会有相应的性能上的开销:
try-catch代码块需要尽可能小,并且要避免使用异常控制代码流程
Java每实例化一个Exception,都会对当时的栈进行快照,这是一个相对比较重的操作
所以当服务出现了反应变慢,吞吐量下降的时候,检查发生最频繁的Exception也是一种思路
可以思考一个问题,对于异常处理编程,不同的编程范式也会影响到异常处理策略,比如,现在非常火热的反应式编程(Reactive Stream),因为其本身是异步、基于事件机制的,所以出现异常情况,决不能简单抛出去;另外,由于代码堆栈不再是同步调用那种垂直的结构,这里的异常处理和日志需要更加小心,我们看到的往往是特定 executor 的堆栈,而不是业务方法调用关系。对于这种情况,你有什么好的办法吗?
答:这种情况下的异常,可以通过重试队列来进行完善;在调用过程中,对于出现的异常可以进行捕获和封装,再进一步延续到异步上下文或者转同步处理时拿到原始的错误和堆栈信息。比如Future Stage使用ExceutionException。