重新检查异常

时间:2010-12-29 12:50:02

标签: java exception exception-handling transactions error-handling

public void foo() {
 begin();
 try {
  ...
  commit();
 } catch (Exception e) {
  rollback();
  throw e;
 }
}

在上面的示例中,由于foo没有throws Exception,因此出现错误。添加它不会使方法的可用性也很好。

最好的方法是什么?如果在没有真正“处理”错误的情况下发生错误,你该怎么办?

9 个答案:

答案 0 :(得分:6)

至少有两种方法可以解决,这些方法通常会组合,具体取决于您希望foo做什么:

1。仅捕获并重新抛出相关的异常

主流中的代码只能抛出很多例外(可能主要是SqlException)。所以只抓住并重新抛出那些,并声明你正在这样做。更重要的是,重新抛出你实际上没有处理的那些(在简化的示例代码中,你没有处理任何代码,但你的真实代码可能更精细)。

请注意,一些例外可能是运行时异常,因此您可能希望将其与下面的内容结合起来。

2。根本不要抓住例外

像这样:

// Signature changes to include any exceptions that really can be thrown
public void foo() throws XYZException, ABCException {
 // A flag indicating that the commit succeeded
 boolean done = false;

 begin();
 try {
  // Don't have any `return` statements in here (or if you do,
  // set `done` to `true` first)

  ...
  commit();
  done = true; // Commit didn't throw an exception, we're done
 } finally {
  // finally clause always happens regardless
  if (!done) {
    // We must be processing an exception; rollback
    try {
      rollback();
    } catch (Exception e) {
      // quash it (e.g., leave this block empty), we don't want
      // to mask the real exception by throwing a different one
    }
  }
 }
}

当然,您的签名需要包含可能在主流中引发的任何异常,但如果我正确理解您,那就是您正在尝试做的事情。

同样,您可能合并这两种方法,因为您可能希望处理某些例外而不是其他例外。

答案 1 :(得分:5)

从Java 8开始,我们使用

/**
 * Cast a CheckedException as an unchecked one.
 *
 * @param throwable to cast
 * @param <T> the type of the Throwable
 * @return this method will never return a Throwable instance, it will just throw it.
 * @throws T the throwable as an unchecked throwable
 */
@SuppressWarnings("unchecked")
public static <T extends Throwable> RuntimeException rethrow(Throwable throwable) throws T {
    throw (T) throwable; // rely on vacuous cast
}

您可以重新抛出已检查的异常,但只能避免编译器检查异常验证。

public void foo() throws MyCheckedException {
 begin();
 try {
  ...
  commit();
 } catch (Exception e) {
  rollback();
  // same as throwing an exception without the compiler knowing.
  Thread.currentThread().stop(e); 
 }
}

在使用stop()之前,您应该阅读http://download.oracle.com/javase/6/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html

  

Thread.currentThread()。stop(e)..在行为上与Java的throw操作相同,但绕过了编译器保证调用方法已经声明它可能抛出的所有已检查异常的尝试:

答案 2 :(得分:3)

用一些未经检查的RuntimeException包装它。

答案 3 :(得分:1)

  

添加它不会使方法的可用性也很好。

即可。它会擅长文档,调用者也会小心处理它。

另见

答案 4 :(得分:0)

我会说在这种情况下回滚正确处理异常。这是少数几个能够抓住并重新投掷的合法案例之一。

简单地捕获和记录异常并不是我想要处理的。在这种情况下,我宁愿看到添加到方法签名中的已检查异常并让它冒出来,而不是重新抛出。

答案 5 :(得分:0)

您可能会抛出RuntimeException的子类 - 它们不需要catch()

答案 6 :(得分:0)

关键是,为什么要从catch块中抛出一个新的Exception。 如果你使用catch,那么在你的catch中处理你的异常。如果必须通过异常通知调用方法,则不要使用try-catch捕获异常,而是使用throws签署方法并让调用者捕获e xception。

或抛出一个RuntimeException,我发现这个想法没那么有用,因为缺乏可读性,那么你不需要用throws签署你的方法。

答案 7 :(得分:0)

在这里,你偶然发现了Java中最大的宗教分裂之一(如果不是更广泛的话)。它可以归结为那些感觉,就像TJ似乎和我一样,检查异常是有价值的,原因很多,VS Rod Johnson / Spring学校在Java的设计中,检查异常在许多情况下使用了不应该,比如关闭结果集或套接字,因为它在许多情况下被错误地使用,它使它们无用,所以所有异常都应该是未选中的。 Spring框架中有许多类是围绕标准Java对象的非常薄的包装器,但是将已检查的异常转换为未选中。它让我疯狂!

无论如何,让我失望,同意TJ所说的一切,但要知道你可能永远找不到“正确”的答案。

答案 8 :(得分:0)

值得一提的是该领域的一些进展。

首先,在Java 7中,只要在外部块中捕获或声明在try块内声明的内容,就可以捕获并抛出泛型异常。所以这会编译:

void test() throws SQLException {
  try { 
    conn.commit();
  } catch (Throwable t) {
    // do something
    throw t;
  }
}

这里有很好的解释:http://docs.oracle.com/javase/7/docs/technotes/guides/language/catch-multiple.html

此处描述了另一种方式:http://blog.jooq.org/2012/09/14/throw-checked-exceptions-like-runtime-exceptions-in-java/

您可能想要使用它的原因通常与lambdas有关,或者从某些泛型方法中抛出异常。当我想要替换重复的构造时,我遇到了这个:

try {
  do_operation
  flag_success
} catch (Throwable e) {
  flag_error
  throw e;
}

使用以下方法:

public static void wrapExec(RunnableT s) {
    try {
        s.run();
        flag_success
    } catch (Throwable t) {
        flag_error
        doThrow(t);
    }
}

因此只用

替换整个try / catch块
wrapExec(()->{do_operation})