【问题标题】:How to prevent exception catching when we need to throw custom exceptions?当我们需要抛出自定义异常时,如何防止异常捕获?
【发布时间】:2018-07-27 07:38:29
【问题描述】:
  void connectOverNetwork() throws Exception {
    try {
      final JSONObject response = make network call;

      if (!response.getBoolean(SUCCESS)) {
        LOG.error("--- foo message ---");
        throw new Exception("message replied with error");
      }
    } catch (final Exception e) {
      LOG.error("---- bar message ---");
      throw new SvcException("failed to connect over network");
    }
  }

在上面的代码中,我抛出了一个带有失败消息的异常。 同时,我也抛出了网络连接失败的错误。

但是,如果我为 !success 抛出异常,它会再次被捕获,从而导致重复记录。我不想打印bar message,如果我只想记录foo message

如何防止它发生?

【问题讨论】:

  • 为什么不能从那里删除?
  • 您使用什么 API 进行网络调用?

标签: java exception exception-handling


【解决方案1】:

try-catch 语句之后验证response

JSONObject response = null;
try {
    response = /* make network call */;
} catch (final Exception e) {
    LOG.error("---- bar message ---");
    throw new SvcException("failed to connect over network");
}

if (!response.getBoolean(SUCCESS)) {
    LOG.error("--- foo message ---");
    throw new Exception("message replied with error");
}

我不建议捕获Exception - 它太笼统了,我建议您将其缩小到更具体的异常类型。

【讨论】:

  • 您应该将捕获的异常包装在 SvcException 中。您还应该在最后一行抛出 SvcException 而不是 Exception。
  • @MiguelMunoz,我没明白。你能澄清一下吗?
  • 当你捕获一个异常并抛出另一个异常时,你应该总是将第一个异常包裹在第二个异常中。您通常可以将其传递给构造函数。如果没有,您可以使用 Throwable 的 initCause() 方法进行设置。请参阅下面的答案。
  • @MiguelMunoz,我可能会也可能不会用一个新的例外来结束原因(我可以提取所需的信息并忘记它,我可能只是对原因不感兴趣)
  • @MiguelMunoz 这当然是一个很好的做法,但它是自愿的,而不是强制性的
【解决方案2】:

如果你把它移到try 块之外怎么办。无论如何,第一个try..catch 的原因是为了从网络调用中捕获任何异常。

JSONObject response = null;
try {
    response = make network call;
} catch (final Exception e) {
    LOG.error("---- bar message ---");
    throw new SvcException("failed to connect over network");
}
if (!response.getBoolean(SUCCESS)) {
    LOG.error("--- foo message ---");
    throw new Exception("message replied with error");
}

【讨论】:

    【解决方案3】:

    创建您自己的异常类型,不要捕获它。

    try {
        do stuff
        if (condition) 
            throw new MyCustomException("error")
    } catch (IOException e) {
        log and rethrow
    }
    

    【讨论】:

      【解决方案4】:

      首先,让我指出您的代码中的一个错误。您的方法声明它抛出异常,但它没有。它抛出 SvcException。所以这就是“投掷”条款应该说的。 (无论如何,你都不应该说“抛出异常”。你应该明确说明它抛出了什么样的异常。)答案的其余部分取决于你的模糊描述“进行网络调用”是否抛出异常。

      如果没有,您的方法应该如下所示:

      void connectOverNetwork() throws SvcException {
        final JSONObject response = makeNetworkCall();
      
        if (!response.getBoolean(SUCCESS)) {
          LOG.error("--- foo message ---");
          throw new SvcException("message replied with error");
        }
      }
      

      但这是不现实的。很有可能,您的“进行网络调用”代码会抛出类似 IOException 的东西。在这种情况下,您的代码应如下所示:

      void connectOverNetwork() throws SvcException {
        try {
          final JSONObject response = makeNetworkCall(); // throws IOException
      
          if (!response.getBoolean(SUCCESS)) {
            LOG.error("--- foo message ---");
            throw new SvcException("message replied with error");
          }
        } catch (final IOException e) {
          LOG.error("--- foo message ---");
          throw new SvcException("failed to connect", e); // wrap e inside SvcException
        }
      }
      

      请注意,我将捕获的 IOException 包装在 SvcException 中。如果您的 SvcException 没有这样做,请重写它以便它可以,或者在抛出它之前调用它的 initCause() 方法。在重新抛出不同的异常时,您应该始终包含原始异常。

      另外请注意,我不会费心抛出、捕获并重新抛出 IOException。当我检测到失败时。我只是抛出我需要抛出的异常。这意味着我需要在两个不同的地方记录 foo 消息。对于大多数事情,应该避免重复一行代码,但对于日志记录,这很好。

      但是这段代码有点乱。我会通过将成功测试与可能的 IOException 分开来清理它。所以我会这样写:

      void connectOverNetwork() throws SvcException {
        JSONObject response; // no need to initialize this.
        try {
          response = makeNetworkCall(); // throws IOException
        } catch (final IOException e) {
          LOG.error("--- foo message ---");
          throw new SvcException("failed to connect", e); // wrap e inside SvcException
        }
      
        if (!response.getBoolean(SUCCESS)) {
          LOG.error("--- foo message ---");
          throw new SvcException("message replied with error");
        }
      }
      

      请注意,响应是在 try 循环之前声明的。它没有被初始化,因为它不可能在没有值的情况下到达!response.getBoolean(SUCCESS) 测试。如果 makeNetworkCall() 抛出异常,它甚至不会到达该行。

      【讨论】:

        猜你喜欢
        • 2015-07-14
        • 2023-03-18
        • 1970-01-01
        • 1970-01-01
        • 2016-02-17
        • 2015-02-13
        • 1970-01-01
        • 2013-06-24
        • 1970-01-01
        相关资源
        最近更新 更多