Java:无法重新抛出异常:未处理的异常类型Exception。

15
我想捕获一个异常,记录它,设置一个标志,然后重新抛出相同的异常。
我有以下代码:
public Boolean doJobWithResult() {
    boolean result = true;
    final Feed feed = Feed.findById(feedId);
    try {
        feed.fetchContents();
    } catch (Exception ex) {
        result = false;
        Logger.info("fetching feed(%d) failed", feedId);
        throw ex;
    }
    return result;
}

但是Eclipse在 throw ex 处报错,提示“未处理异常类型 Exception”,并建议我在其周围添加 try-catch 块。

实际上,我希望调用此方法的进程来处理异常,而不是自己处理它...如果一切正常,我只想返回 true,并在有异常时记录日志。

另一方面,我可以将异常包装在另一个异常中,但我无法抛出相同的异常...

有什么想法吗?

10个回答

8

您的doJobWithResult方法需要声明可以抛出异常:

public Boolean doJobWithResult() {

变成

public Boolean doJobWithResult() throws Exception {

8
我认为这里有几个要点需要提到:
  1. 你可以选择让 doJobWithResult() 在成功时返回 true,失败时返回 false;或者在成功时不返回任何东西,在失败时抛出异常。两种情况不能同时存在。如果是第一种情况,捕获异常、记录日志并返回 false;如果是第二种情况,改变方法签名以返回 void,抛出异常并在调用方处理。
  2. 捕获异常、记录日志并重新抛出异常是一个错误的做法。为什么呢?因为方法的潜在调用者无法知道你已经记录了日志,他可能会再次记录。要么抛出异常(这样调用者必须处理异常),要么捕获并处理异常(记录日志)。
  3. 需要注意的是,抛出 Exception 并不能让方法的调用者知道什么可能会在方法中出错,最好抛出更具体的异常,或者将异常包装成自定义异常并重新抛出。
  4. 此外,如果抛出 Exception,调用者可能会试图捕获 Exception 而忽略它继承的 RuntimeException,这可能不是期望的行为。

所有的答案都非常有用,我认为这个答案也提供了一些关于如何处理异常的提示... - opensas
大部分都是正确的,但我想提出唯一的反对意见是关于#2。这高度取决于您的应用程序结构,但在构建REST API的情况下,分层处理异常可以很好地工作。例如,您可以在抛出异常时使用日志记录来处理异常,然后像#3中建议的那样抛出一个新的包装器异常。类似RecoverableClientExceptionUnrecoverableClientException的东西可以让调用代码知道如何继续进行。包装器还可以帮助决定发送给客户端什么类型的响应和HTTP状态码。 - Jeremiah Shore

6
如果您将throws Exception添加到方法签名中,可以抛出相同的异常。否则,您可以抛出RuntimeException
public Boolean doJobWithResult() { 
    boolean result = true; 
    final Feed feed = Feed.findById(feedId); 
    try { 
        feed.fetchContents(); 
    } catch (Exception ex) { 
        result = false; 
        Logger.info("fetching feed(%d) failed", feedId); 
        throw new RuntimeException(ex); 
    } 
    return result; 
} 

在这种情况下,您不需要指示public Boolean doJobWithResult() 抛出任何异常,但请确保稍后正确处理它(捕获或期望线程停止...毕竟这是运行时异常)。

3

由于Exception是一个已检查的异常,捕获Exception的替代方法是将你的方法声明为抛出它:

public Boolean doJobWithResult() throws Exception {
    // ...
}

简单明了的回答。我给你10分奖励 :) - Sarath Subramanian

3
如果doJobWithResult不需要处理异常,则删除catch块并在方法签名中添加“throws Exception”。异常记录可以在必须处理异常的类/方法中的相应try/catch块中完成。

2

在catch块中将结果设置为false是没有必要的,因为该值不会被返回(因为我们正在抛出异常)。

您的方法还应声明它会抛出一个异常,这样客户端就会被迫处理它。

另外考虑使用一个更具体的异常,在这种特定情况下会被抛出。


1
在你的方法中添加throws Exception。你也不需要在catch块中添加result = false;

1

我认为,如果feed.fetchContents()方法失败无法恢复,你处理这个异常的方式确实非常适当。(最好停止而不是继续) 除此之外,我建议你使用更具体的异常层次结构。

另外,我从《Effective Java》这本书中学到的另一件事是,如果你编写这样的方法,你必须在注释中记录@throw(原因)。


0
我花了最后一个小时寻找它,因为即使是完整参考书也没有明确提到这一点:未处理的 throw ThrowableInstance 仅适用于未经检查的异常。只有运行时异常是未经检查的。通过未处理,我的意思是像这样:
class ThrowDemo {
  static void demoproc() {
    try {
      throw new NullPointerException("demo");
    } catch(NullPointerException e) {
      System.out.println("Caught inside demoproc.");
      throw e; // re-throw the exception
    }
  }

  public static void main(String args[]) {
    try {
      demoproc();
    } catch(NullPointerException e) {
      System.out.println("Recaught: " + e);
    }
  }
}

此示例文字来自《Java编程语言实战教程》第九版。

第一个throw语句,即throw new NullPointerException("demo");会被下面的catch块处理,但第二个throw语句,即throw e;demoproc()方法中未被处理。这段代码能够成功编译运行是因为NullPointerException是一个运行时/未检查异常。如果e实例是一个已检查异常或者是Exception类的实例,那么你将会得到一个错误提示,指出异常e未被处理,你将不得不在demoproc()内部进行处理,或者在方法签名中使用throws显式地声明demoproc()会抛出一个异常。


0

你可以抛出一个未经检查的异常

 Logger.info("fetching feed(%d) failed", feedId);
 throw new RuntimeException(ex);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接