Tips
书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code
注意,书中的有些代码里方法是基于Java 9 API中的,因此JDK 最好下载 JDK 9以上的版本。java
许多Java程序员不喜欢检查异常,但若是使用得当,他们能够改进API和程序。 与返回码和未检查异常不一样,它们迫使程序员处理异常问题,加强可靠性。 也就是说,在API中过分使用检查异常会使它们使用起来不那么使人愉快。 若是方法抛出检查异常,则调用它的代码必须在一个或多个catch块中处理它们,或者声明抛出它们并向上传播。 不管哪一种方式,它都会给API的使用者带来负担。这种负担在Java 8中加剧了,由于抛出检查异常的方法不能直接在Stream中使用(条目45——48)。git
若是不能经过正确使用API来防止异常状况,而且使用API的程序员在遇到异常时能够采起一些有用的操做,那么这种负担是合理的。除非知足这两个条件,不然可使用未检查异常。做为最后的检验(litmus test),能够问问本身:程序员将如何处理异常。这是最好的办法吗?程序员
} catch (TheCheckedException e) { throw new AssertionError(); // Can't happen! }
或者这样:github
} catch (TheCheckedException e) { e.printStackTrace(); // Oh well, we lose. System.exit(1); }
若是程序员不能作得更好,则须要一个未检查异常。app
若是方法抛出的检查异常是唯一的,那么检查异常给程序员带来的额外负担就会大得多。若是还有其余方法,则该方法必须已经出如今try块中,而且最多须要另外一个catch块。若是一个方法抛出单个检查异常,那么这个异常就是该方法必须出如今try块中,而且不能直接在Stream中使用。在这种状况下,有必要问问本身是否有办法避免检查异常。性能
消除检查异的最简单方法是返回所需结果类型的Optional(条目 55)。该方法只返回一个空的Optional,而不是抛出一个检查的异常。这种方法的缺点是,该方法不能返回任何详细说明其没法执行所需计算的额外信息。相反,异常具备描述性类型,而且能够导出方法来提供额外的信息(条目 70)。测试
还能够经过将抛出异常的方法分解为两个方法,将检查异常转换为未检查异常,第一个方法返回一个boolean值,表示是否抛出异常。 这个API重构将调用序列:线程
// Invocation with checked exception try { obj.action(args); } catch (TheCheckedException e) { ... // Handle exceptional condition }
转换为:code
// Invocation with state-testing method and unchecked exception if (obj.actionPermitted(args)) { obj.action(args); } else { ... // Handle exceptional condition }
这种重构并不老是合适的,可是它可使API更加温馨。 虽而后者调用序列并不比前者更漂亮,但重构的API更灵活。 若是程序员知道调用将成功,或者知足于让线程在失败时终止,那么重构也容许这个简单的调用序列:对象
obj.action(args);
若是怀疑普通的调用序列会成为常态,那么API重构多是合适的。生成的API本质上与条目 69中的状态测试方法API,而且适用与相同的警告:若是要在没有外部同步的状况下同时访问对象,或者被外部转换状态,则此重构是不合适的,由于对象的状态多是在对actionPermitted
和action
的调用之间进行更改。若是单独的actionPermitted
方法会重复action
方法的工做,则可能会因性能缘由而排除重构。
总之,若是谨慎使用,检查异常能够提升程序的可靠性;当过分使用,会使API难以使用。若是调用者没法从失败中恢复,则抛出未检查异常。若是恢复是可能的,而且但愿强制调用者处理异常条件,那么首先考虑返回Optional的。只有当在失败的状况下,没法提供充分的信息时,才应该抛出一个检查的异常。