用Java编写带有清理操作的catch块

用Java编写带有清理操作的catch块,java,exception,exception-handling,Java,Exception,Exception Handling,我在Java中找不到任何关于catch块的建议,这些块涉及一些本身可能引发异常的清理操作 经典的例子是stream.close(),我们通常在finally子句中调用它,如果它引发异常,我们要么在try-catch块中调用它来忽略它,要么声明它为rethrown 但一般来说,我如何处理以下情况: public void doIt() throws ApiException { //ApiException is my "higher level" exception try { do

我在Java中找不到任何关于catch块的建议,这些块涉及一些本身可能引发异常的清理操作

经典的例子是
stream.close()
,我们通常在
finally
子句中调用它,如果它引发异常,我们要么在try-catch块中调用它来忽略它,要么声明它为rethrown

但一般来说,我如何处理以下情况:

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子句中声明较低级别的异常
  • 是否在清理操作期间忽略异常

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

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

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

接下来,如果确实需要抛出次要异常,请确定次要异常的各种原因是否重要。这将是罕见的。因此,将次要原因设置为主要原因(使用适当的构造函数或initCause()

最后,如果您必须抛出次进程,并保留次进程和主进程的完整堆栈跟踪,那么您就需要创建一个自定义异常来处理这种情况。这很难看,因为您可能希望从不同的父类派生。如果确实出现这种情况,我建议创建一个助手类,该类能够填充目标异常的堆栈跟踪,从而基于这两个异常生成一个有意义的跟踪(确保对次要异常使用缩进,这样嵌套的异常很容易分离)


但最重要的是,我建议您使用log和rethrow主要范式。专注于解决主要问题,次要问题通常会自行解决(这里的经典示例是IO异常,它将事情弄得如此糟糕,以至于调用close()也无法成功)。

我不确定。首先,有两个异常源,因此我不确定最后放在哪里;其次,我猜您指的是Throwable上的setStackTrace()方法。我们没有接受堆栈跟踪的异常构造函数。