第73条 抛出与抽象对应的异常
-
更高层的实现应该捕获底层的异常,同时抛出可以按照高层抽象进行解释的异常。这种做法称为*异常转义
// 来自AbstractSequentialList的get方法 public E get(int index) { ListIterator<E> i = listIterator(index); try { return i.next(); } catch (NoSuchElementException e) { throw new IndexOutOfBoundsException("Index: " + index); } }
-
一种特殊的异常转译形式被称为异常链,如果底层的异常对于调试导致高层异常的问题非常有帮助,使用异常链就很合适。底层的异常原因被传到高层的异常,高层的异常提供访问方法
Throwable.getCause
来获得底层的异常try { ... // Use lower-level abstraction to do our bidding } catch (LowerLevelException cause) { throw new HigherLevelException(cause); }
对于没有支持链的异常,可以利用
Throwable
的initCause
方法设置原因尽管异常转译与不加选择地从底层传递异常的做法相比有所改进,但是它也不能被滥用。可以在给底层传递参数之前,检查更高层方法的参数的有效性,从而避免底层方法抛出异常
思考
- 我们编写业务代码,很少会自己构造异常链,我们基本上也不会主动抛出异常。我们一般情况下都是会使用
getCause
方法判断异常原因。比如我们会在transaction结束的时候catch
住异常,不断getCause
拿到最底层的报错原因,判断是否是数据库报了乐观锁,从而进入不同的异常处理flow
第74条 每个方法抛出的所有异常都要建立文档
- 始终要单独地声明受检的异常,使用
@throws
标记,准确地记录下抛出每个异常的条件 - 永远不要声明一个方法
throws Exception
,或者throws Throwable
- 使用
@throws
标签记录下一个方法可能抛出的每个未受检异常,但是不要在未受检异常上使用throws
关键字 - 如果一个类中的许多方法出于同样的原因而抛出同一个异常,则可以在类的文档注释中记录异常
思考
- 一般的开发中,我很少一个方法会
throws
一个Exception
,最多会在参数校验失败或者其它极端的情况throw IllegalArgumentException
,这其实一般情况写也是不会发生的。这种throws
一般出现在框架代码中,给其它的业务开发者使用,业务代码里很少会看到一个方法会看到throws