第10章 异常
69,只针对异常的情况才使用异常
异常应该只用于异常的情况;
它们永远不应该用于正常的控制流。
设计良好的 API 不应该强迫它的客户端为了正常的控制流而使用异常。
70,对可恢复的情况使用受检异常,对编程错误的情况使用运行时异常
受检异常:强迫工程师解决的异常。
你实现的所有未受检抛出结构都应该是 RuntimeExecption 的子类。
对于可恢复的情况抛出受检异常。
对于程序错误,抛出运行时异常。
不确定是否可恢复,则抛出未受检异常。
不要定义任何既不是受检异常也不是运行时异常的抛出类型。
要在受检异常上提供方法,以便协助恢复。
71,避免不必要地使用受检异常
在谨慎使用的情况下,受检异常可以提高程序的可读性;
如果过度使用,将会使 API 使用起来十分痛苦。如果调用者无法恢复失败,则应该抛出未受检异常。
如果可以恢复,并且想要迫使调用者处理异常的条件,首选应该返回一个 optional 值。
当且仅当万一失败时,这些无法提供足够的信息,才应该抛出受检异常。
72,优先使用标准的异常
73,抛出与抽象对应的异常
更高层的实现应该破获低层异常,同时抛出可以按照高层抽象进行解释的异常。这种方法叫做异常转译。
异常链:常常会再捕获一个异常后抛出另外一个异常,并且希望把异常原始信息保存下来,这被称为异常链
尽管异常转译与不加选择地从低层传递异常地做法相比有所改进,但是也不能滥用它。
74,每个合法抛出的异常都要建立文档
75,在细节消息中包含失败 - 捕获信息
为了捕获异常,异常地细节信息应该包含 “ 对该异常有贡献 ” 的所有参数和域的值。
千万不要在异常中包含密码,密钥以及类似的信息!
堆栈轨迹
76,努力使失败保持原子性
一般而言,失败的方法调用应该使对象保持在被调用之前的状态,具有这种属性的方法被称为具有失败原子性。
如果违反这种规则,API 文档就应该清楚的指明对象将会处于什么样的状态。
77,不要忽略异常
空的 catch 块会使异常达不到应有的目的。
如果选择忽略异常,catch 块就应该包含一条注释,说明为什么这样做,并且命名为 ignored。