使用Java中的清理操作编写catch块

时间:2010-05-08 02:18:06

标签: java exception exception-handling

我无法在Java中找到任何关于catch块的建议,这些建议涉及一些本身可能会抛出异常的清理操作。

经典的例子是我们通常在stream.close()子句中调用的finally,如果抛出异常,我们要么通过在try-catch块中调用它来忽略它,要么将它声明为被重新抛出。

但总的来说,我如何处理以下案件:

public void doIt() throws ApiException { //ApiException is my "higher level" exception
  try {
    doLower();
  } catch(Exception le) {
    doCleanup(); //this throws exception too which I can't communicate to caller
    throw new ApiException(le);
  }
}

我能做到:

catch(Exception le) {
  try {
    doCleanup();
  } catch(Exception le1) {
  //ignore?
  //log?
  }
  throw new ApiException(le); //I must throw le
}

但这意味着我将不得不进行一些日志分析,以了解清理失败的原因。

如果我这样做了:

catch(Exception le) {
  try {
    doCleanup();
  } catch(Exception le1) {
    throw new ApiException(le1);
  }

这会导致失去le让我在第一个位置的拦截区。

人们在这里使用的一些习语是什么?

  • 在throws子句中声明较低级别的异常?
  • 在清理操作期间忽略异常?

2 个答案:

答案 0 :(得分:3)

首先,确定你是否真的需要从finally块中抛出 - 仔细考虑一下 - 在close()调用失败的情况下......好吧,记录它很好 - 但是什么可以更高层你的API真的能解决这个问题吗?因此,对于99%的情况,您将记录次要,然后重新抛出主要。

接下来,如果您确实需要抛出辅助节点,请确定辅助异常的各种原因是否重要。它们很少见。因此,将辅助的原因设置为主要(使用适当的构造函数或initCause())。

最后,如果您必须抛出辅助节点,并保留辅助节点和主节点的完整堆栈跟踪 - 那么您将创建一个自定义异常来处理这种情况。这很丑陋,因为您可能希望从不同的父类派生。如果出现这种情况,我建议创建一个辅助类,它能够填充目标异常的堆栈跟踪,以便根据两个异常生成有意义的跟踪(确保对辅助使用缩进,因此嵌套异常是容易拉开。

但大多数情况下,我建议您使用log-and-rethrow-primary范例。专注于修复主要问题,而次要问题通常会自行解决(这里的典型例子是一个IO异常,它会严重影响到对close()的调用也不会成功)。

答案 1 :(得分:0)

使用finally块。如果您担心吞咽堆栈跟踪,请执行以下操作:

Exception ex = new Exception(le.stackTrace());