【问题标题】:Java exceptions wrapping: bad practice?Java 异常包装:不好的做法?
【发布时间】:2017-11-16 08:51:01
【问题描述】:

来自 PHP 世界,那里只有一种编写异常处理的方法。我发现 Java 中的异常包装有点“丑陋”:

public void exampleOneException(String input) throws MyBusinessException {
    try {
        // do something
    } catch (NumberFormatException e) {
        throw new MyBusinessException("Error...", e);
    }
}

我更喜欢使用这种风格:

public void exampleTwoException() {
    try {
        // do something
    } catch (MyBusinessException e) {
        log.error("Error...: " + e);
    } catch (NumberFormatException e) {
        log.error("Error...: " + e);
    }
}

这些处理异常的方法有什么不同或最佳实践吗?

【问题讨论】:

  • 调用exampleOneException的方法可以捕捉到MyBusinessException,根据自己的需求进行处理。调用exampleTwoException 的方法无法知道发生了什么问题。
  • 它们在做完全不同的事情。您应该使用的方法取决于您希望如何响应错误 - 例如只需在本地记录它们、转发它们,或在发生错误时更改行为的专用业务逻辑。
  • 这两个代码 sn-ps 没有做同样的事情。在一个中,您正在重新抛出异常,而在另一个中,您只是在记录它。

标签: java exception exception-handling software-quality


【解决方案1】:

这些都是适用于两种不同场景的有效方法。

在第一种情况下,该方法无法对异常执行任何智能操作,但它必须向上“报告”它。这样,调用者可以捕获异常并决定如何处理它(例如取消流程、向用户弹出消息、记录它等)

在第二种情况下,您捕获异常并将其记录下来,从而有效地向调用者隐藏故障。如果调用者并不真正关心操作是否成功,这种类型的处理可能很有用。

【讨论】:

  • 如果您确定操作会成功,您也可以在 to catch 子句中不写入任何内容。它被认为是糟糕的编程,但仅仅因为人们懒惰而被广泛使用^^
【解决方案2】:

第一个示例通常被视为更好的方法。

您也不应将MyBusinessException 视为包装 NumberFormatException,而应将NumberFormatException 视为MyBusinessException原因

异常应该适合被暴露的接口。接口的调用者不需要知道或处理实现细节。除非在调用 exampleOneException 时将 NumberFormatException 作为一种错误类型真正有意义,否则它应该被转换为更合适的异常。

更具体的示例通常包括不同的实现,其中不应要求接口的用户处理实现细节(甚至在编译时可能不知道)。

interface MyRepository {
    Object read(int id) throws ObjectNotFoundException;
}

// a sql backed repository
class JdbcRepository implements MyRepository {
    public Object read(int id) throws ObjectNotFoundException {
        try { ... }
        catch (SQLException ex) {
            throw new ObjectNotFoundException(ex);
        }
    }
 }

// a file backed repository
class FileRepository implements MyRepository {
    public Object read(int id) throws ObjectNotFoundException {
        try { ... }
        catch (FileNotFoundException ex) {
            throw new ObjectNotFoundException(ex)
        }
    }
}

因为接口声明了它可以返回的错误类型,所以该接口的客户端可以是一致和合理的。添加代码来处理FileNotFoundExceptionSQLException 然后实际的实现可能是,或者两者都不是,这并不好。

考虑在FileRepository 的实现中是否有多个地方可能抛出FileNotFoundException。这是否意味着它们中的每一个都意味着找不到对象

在考虑选项二 exampleTwoException 时,重要的是要意识到您的 catch 块实际上是在说明发生的任何错误的影响都已得到缓解。 种情况下,检查的异常被合理地忽略了,但更可能的是一个简单的

try { ... }
catch (SomeException ex) {
    log.error("caught some exception", ex);
}

实际上是开发人员没有考虑异常后果的结果,或者代码应该包含FIXME

当您看到catch (Exception ex) 或不合情理的catch (Throwable ex) 时,更是如此。

最后,您是否想成为一个挖掘应用程序的人,找到您需要添加(但)另一个 catch 块来处理新实现的所有地方?也许这是catch (Exception ex)的原因...

总是抛出适合抽象的异常

【讨论】:

    【解决方案3】:

    我想说NumberFormatExceptionMyBusinessException 都很有用,但在不同的情况下。

    它们通常出现在类层次结构的不同级别:例如,NumberFormatException 是一个较低级别的异常,如果它的用户没有权限,您可能不想在更高级别(例如用户界面)公开它从中恢复。在这种情况下,直接抛出 MyBusinessException 并显示一条信息性消息会更优雅,例如说明上一步中的某些内容供应不当或发生了一些内部处理错误,他/她需要重新启动该过程。

    另一方面,如果您的函数用于中间级别(例如 API)并且开发人员有办法从异常行为中恢复,NumberFormatException 更有用,因为它可以通过编程方式处理,并且应用程序的流程可能会以最小的中断继续(例如,提供默认的有效号码)。或者,这可能表明代码中存在应修复的缺陷/错误。

    有关如何遵循使用异常的最佳实践的详细信息,请阅读 Item 61 - Throw exceptions appropriate to the abstractionEffective Java by Joshua Bloch

    【讨论】:

      【解决方案4】:

      从干净和可靠的代码的角度来看,您并没有朝着正确的方向前进。
      首先,您不需要对方法标头已经抛出的Exception 使用 try/catch。因此,在您的第一种情况下,代码应如下所示:

       public void exampleOneException(String input) throws MyBusinessException {
          // do something
       }
      

      这更漂亮不是吗? 在上述情况下,如果MyBusinessException 会发生,那么异常将被吞没,因此用户将永远看不到发生了什么。如果您希望这种情况发生,那么这是您的最佳做法。

      第二种情况很清楚。您捕获两个不同的异常并处理它们 (通过记录)一般来说这是一种更好的做法。

      因此,两种不同情况的最佳实践取决于您想要实现的目标。关于美,这完全是主观的。

      【讨论】:

      • 当我需要 finally 使用时需要 try/catch:就像我想关闭数据库连接而不是依赖 Java 垃圾收集器。
      • 简单解释:如果你想处理异常,需要try/catch。如果您不关心传播或处理,那么您可以在方法的标头上使用throws。使用finally 预设了处理意图。
      • 好吧,如果我们关心代码质量并因此使用 linting 库 (sonarlint),那么使用 finally 是一种“好习惯”,否则我们会得到 linter 显示错误:“资源应该关闭”。 . 但这是另一个话题..
      • 你是对的,使用 finally 实际上是一种很好的做法。虽然我的回答指出了另一个问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-19
      • 2010-11-01
      • 2011-11-26
      • 1970-01-01
      • 1970-01-01
      • 2015-07-10
      • 1970-01-01
      相关资源
      最近更新 更多