何时捕获异常与何时引发异常?

2022-08-31 13:17:56

我已经用Java编码了一段时间了。但有时,我不明白何时应该引发异常,何时应该捕获异常。我正在从事一个项目,其中有很多方法。层次结构是这样的 -

Method A will call Method B and Method B will call some Method C and Method C will call Method D and Method E.

所以目前我正在做的是 - 我在所有方法中抛出异常,并在方法A中捕获它,然后记录为错误。

但我不确定这是否是正确的方法?或者我应该开始在所有方法中捕获异常。这就是为什么这种混乱始于我的 - 我什么时候应该抓住异常与何时应该抛出异常。我知道这是一个愚蠢的问题,但不知何故,我正在努力理解这个主要概念。

有人可以给我一个详细的例子,以便我的概念得到澄清吗?在我的情况下,我是否应该继续抛出异常,然后在主调用方法A中捕获它?When to catch the Exception vs When to throw the Exceptions


答案 1

当您处于知道要执行的操作的方法中时,应捕获异常。

例如,暂时忘记它的实际工作原理,假设您正在编写一个用于打开和读取文件的库。

所以你有一个类,说:

public class FileInputStream extends InputStream {
    public FileInputStream(String filename) { }
}

现在,假设该文件不存在。你该怎么办?如果你正在努力思考答案,那是因为没有一个......不知道该怎么处理这个问题。因此,它将其抛向链上,即:FileInputStream

public class FileInputStream extends InputStream {
    public FileInputStream(String filename) throws FileNotFoundException { }
}

现在,假设有人正在使用您的库。它们可能具有如下所示的代码:

public class Main {
    public static void main(String... args) {
        String filename = "foo.txt";
        try {
            FileInputStream fs = new FileInputStream(filename);

            // The rest of the code
        } catch (FileNotFoundException e) {
            System.err.println("Unable to find input file: " + filename);
            System.err.println("Terminating...");
            System.exit(3);
        }
    }
}

在这里,程序员知道该怎么做,所以他们捕获异常并处理它。


答案 2

在两种情况下,您应该捕获异常。

1. 在尽可能低的水平

这是您与第三方代码集成的级别,例如ORM工具或执行IO操作的任何库(通过HTTP访问资源,读取文件,保存到数据库,您可以命名它)。也就是说,将应用程序的本机代码留给与其他组件交互的级别。

在此级别,可能会发生您无法控制的意外问题,例如连接失败和文件锁定。

您可能希望通过捕获 来处理数据库连接失败,以便在几秒钟后重试。访问文件时的异常也是如此,该文件目前可能被进程锁定,但在下一刻可用。TimeoutException

此方案中的准则是:

  • 仅处理特定异常,如 或 。从不处理泛型异常(类型SqlTimeoutExceptionIOExceptionException)
  • 只有当您有有意义的事情要做时,才处理它,例如重试,触发补偿操作或向异常添加更多数据(例如上下文变量),然后重新抛出它
  • 不在此处执行日志记录
  • 让所有其他异常冒泡,因为它们将由第二种情况处理

2. 在尽可能高的水平上

这将是在将异常直接抛给用户之前可以处理异常的最后一个位置。

您在这里的目标是记录错误并将详细信息转发给程序员,以便他们能够识别并更正错误。添加尽可能多的信息,记录下来,然后向用户显示道歉消息,因为他们可能对此无能为力,特别是如果这是软件中的错误。

第二种情况下的准则是:

  • 处理泛型异常类
  • 从当前执行上下文中添加更多信息
  • 记录错误并通知程序员
  • 向用户道歉
  • 尽快解决

这些准则背后的原因

首先,异常代表不可逆的错误。它们表示系统中的错误,程序员犯的错误或应用程序无法控制的情况。

在这些情况下,用户通常很少或根本无法执行任何操作。因此,您唯一能做的就是记录错误,采取必要的补偿措施,并向用户道歉。如果这是程序员犯的错误,最好让他们知道并修复它,努力实现更稳定的版本。

其次,尝试 catch 块可以屏蔽应用程序执行流,具体取决于它们的使用方式。块具有与 及其伴随块类似的功能,这会导致应用程序执行流从一个点跳转到另一个点。try catchlabelgoto


何时引发异常

在开发库的上下文中更容易解释。当你遇到错误时,你应该抛出,除了让API的消费者知道它,让他们决定之外,你别无他法。

假设你是某个数据访问库的开发人员。当您遇到网络错误时,除了引发异常之外,您无能为力。从数据访问库的角度来看,这是一个不可逆转的错误。

当您开发网站时,这是不同的。您可能会捕获此类异常以进行重试,但如果您希望在从外层收到无效参数时引发异常,因为它们应该已在那里进行验证。

这在表示层中也有所不同,您希望用户提供无效的参数。在这种情况下,您只需显示友好的消息,而不是引发异常。


https://roaddd.com/the-only-two-cases-when-you-should-handle-exceptions/