在 Java 中,何时应创建已检验的异常,何时应将其设置为运行时异常?

2022-08-31 10:32:05

可能的重复:
何时选择选中和未选中的例外

何时应创建已检查的异常,何时应创建运行时异常?

例如,假设我创建了以下类:

public class Account {
    private float balance;

    /* ... constructor, getter, and other fields and methods */

    public void transferTo(Account other, float amount) {
        if (amount > balance)
            throw new NotEnoughBalanceException();
        /* ... */
    }
}

我应该如何创建我的?它应该扩展还是?或者我应该改用?NotEnoughBalanceExceptionExceptionRuntimeExceptionIllegalArgumentException


答案 1

在这个问题上有很多分歧。在我的上一份工作中,我们遇到了一些真正的问题,运行时异常被遗忘,直到它们出现在生产中(agedwards.com),所以我们决定专门使用检查的异常。

在我目前的工作中,我发现有很多人在很多或所有情况下都支持运行时异常。

以下是我的想法:使用CheckedExceptions,我被迫在编译时至少承认调用方中的异常。对于运行时异常,我不会被编译器强迫,而是可以编写一个单元测试来让我处理它。由于我仍然相信越早捕获错误,修复它就越便宜,因此出于这个原因,我更喜欢CheckedExceptions。

从哲学的角度来看,方法调用在某种程度上是调用方和被调用方之间的契约。由于编译器强制执行传入的参数类型,因此让它强制执行输出类型的似乎是对称的。即,返回值或异常。

我的经验告诉我,当我使用检查的异常时,我会获得更高的质量,即正常工作的代码。检查的异常可能会使代码混乱,但有一些技术可以解决这个问题。我喜欢在传递层边界时转换异常。例如,如果我从我的持久性层中丢失,我想将SQL异常转换为持久性异常,因为下一层不应该关心我是否持久化到SQL数据库,而是想知道某些内容是否无法持久化。我使用的另一种技术是创建一个简单的异常层次结构。这让我可以在一层上写出更清晰的代码,因为我可以捕获超类,并且只在真正重要的时候处理各个子类。


答案 2

总的来说,我认为Joshua Bloch在 Effective Java 中提出的建议最能概括您问题的答案:对可恢复条件使用已检查的期望,对编程错误使用运行时异常(第 2 版第 58 项)。

因此,在这种情况下,如果您真的想使用异常,则应将其选中。(除非 的文档非常清楚地表明,如果不首先使用其他方法检查足够的平衡,则不得调用该方法 - 但这似乎有点尴尬。transferTo()Account

但还要注意第 59 项:避免不必要地使用已检查的异常和 57:仅对异常情况使用异常。正如其他人所指出的,这种情况可能根本不需要例外。如果没有足够的信用额度,请考虑返回(或者可能是包含有关所发生事件的详细信息的状态对象)。false