抓住Throwable是不好的做法?

时间:2011-05-21 17:14:19

标签: java exception-handling throwable

抓住Throwable是不好的做法?

例如:

try {
    // Some code
} catch(Throwable e) {
    // handle the exception
}

这是一种不好的做法,还是应该尽可能具体?

15 个答案:

答案 0 :(得分:86)

您需要尽可能具体。否则不可预见的错误可能会以这种方式蔓延开来。

此外,Throwable也涵盖Errorusually no point of return。你不想捕获/处理它,你希望你的程序立即死亡,以便你可以正确地修复它。

答案 1 :(得分:33)

这是一个坏主意。事实上,即使抓住Exception通常也是一个坏主意。让我们考虑一个例子:

try {
    inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(Throwable e) {
    inputNumber = 10; //Default, user did not enter valid number
}

现在,假设getUserInput()阻塞了一段时间,另一个线程以最坏的方式阻止你的线程(它调用thread.stop())。您的catch块将捕获ThreadDeath错误。这太糟糕了。捕获该异常后代码的行为很大程度上是未定义的。

捕获异常会出现类似的问题。可能getUserInput()由于InterruptException而失败,或者在尝试记录结果时出现权限被拒绝的异常,或者各种其他失败。你不知道出了什么问题,因为那样,你也不知道如何解决这个问题。

您有三个更好的选择:

1 - 准确捕捉您知道如何处理的例外情况:

try {
    inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(ParseException e) {
    inputNumber = 10; //Default, user did not enter valid number
}

2 - 重新抛出您遇到的任何异常并且不知道如何处理:

try {
    doSomethingMysterious();
} catch(Exception e) {
    log.error("Oh man, something bad and mysterious happened",e);
    throw e;
}

3 - 使用finally块,这样您就不必记得重新抛出:

 Resources r = null;
 try {
      r = allocateSomeResources();
      doSomething(r);
 } finally {
     if(r!=null) cleanUpResources(r);
 }

答案 2 :(得分:19)

另请注意,当您抓住Throwable时,您还可以抓住需要特殊处理的InterruptedException。有关详细信息,请参阅Dealing with InterruptedException

如果您只想捕获未经检查的异常,您可能还会考虑此模式

try {
   ...
} catch (RuntimeException exception) {
  //do something
} catch (Error error) {
  //do something
}

这样,当您修改代码并添加可以抛出已检查异常的方法调用时,编译器会提醒您,然后您可以决定如何处理此情况。

答案 3 :(得分:13)

如果您绝对不能从方法中获得异常气泡,那么这不是一个坏习惯。

如果您真的无法处理异常,这是一种不好的做法。最好在方法签名中添加“throws”,而不仅仅是catch和re-throw,或者更糟糕的是,将它包装在RuntimeException中并重新抛出。

答案 4 :(得分:13)

直接来自Error类的javadoc(建议不要捕获这些):

 * An <code>Error</code> is a subclass of <code>Throwable</code> 
 * that indicates serious problems that a reasonable application 
 * should not try to catch. Most such errors are abnormal conditions. 
 * The <code>ThreadDeath</code> error, though a "normal" condition,
 * is also a subclass of <code>Error</code> because most applications
 * should not try to catch it. 

 * A method is not required to declare in its <code>throws</code> 
 * clause any subclasses of <code>Error</code> that might be thrown 
 * during the execution of the method but not caught, since these 
 * errors are abnormal conditions that should never occur. 
 *
 * @author  Frank Yellin
 * @version %I%, %G%
 * @see     java.lang.ThreadDeath
 * @since   JDK1.0

答案 5 :(得分:9)

如果你使用过度热情地抛出错误的库,有时候需要捕获Throwable,否则你的库可能会杀死你的应用程序。

但是,在这些情况下,最好只指定库引发的特定错误,而不是所有Throwables。

答案 6 :(得分:5)

这取决于您的逻辑或更具体的选择/可能性。如果您可以以有意义的方式做出任何特定的异常,您可以先捕获它,然后这样做。

如果没有,并且你确定你会对所有异常和错误做同样的事情(例如退出并带有错误消息),那么抓住throwable就没有问题。

通常第一种情况成立,你不会抓住扔掉的东西。但仍有很多案例可以解决它的问题。

答案 7 :(得分:5)

Throwable是可抛出的所有类的基类(不仅仅是异常)。如果你遇到OutOfMemoryError或KernelError,你几乎无能为力(见When to catch java.lang.Error?

捕获例外就足够了。

答案 8 :(得分:5)

虽然它被描述为一种非常糟糕的做法,但您有时会发现罕见案例,它不仅有用而且是强制性的。这是两个例子。

在Web应用程序中,您必须向用户显示含义完整错误页面。 此代码确保发生这种情况,因为它是围绕所有请求handeler(servlet,struts操作或任何控制器......)的大try/catch

try{
     //run the code which handles user request.
   }catch(Throwable ex){
   LOG.error("Exception was thrown: {}", ex);
     //redirect request to a error page. 
 }

}

作为另一个例子,假设您有一个服务类,为资金转移业务提供服务。如果转移完成,此方法将返回TransferReceipt,如果不能,则返回NULL

String FoundtransferService.doTransfer( fundtransferVO);

现在想象您从用户那里获得List资金转账,您必须使用以上服务来完成所有这些工作。

for(FundTransferVO fundTransferVO : fundTransferVOList){
   FoundtransferService.doTransfer( foundtransferVO);
}

但是如果发生任何异常会发生什么?您不应该停止,因为一次转移可能是成功的而且可能不是,您应该继续通过所有用户List,并将结果显示给每次转移。所以你最终得到了这段代码。

for(FundTransferVO fundTransferVO : fundTransferVOList){
    FoundtransferService.doTransfer( foundtransferVO);
 }catch(Throwable ex){
    LOG.error("The transfer for {} failed due the error {}", foundtransferVO, ex);
  }
}

您可以浏览许多开源项目,以查看throwable是否真正被缓存和处理。例如,搜索tomcatstruts2primefaces

https://github.com/apache/tomcat/search?utf8=%E2%9C%93&q=catch%28Throwable https://github.com/apache/struts/search?utf8=%E2%9C%93&q=catch%28Throwable https://github.com/primefaces/primefaces/search?utf8=%E2%9C%93&q=catch%28Throwable

答案 9 :(得分:3)

问题有点模糊;你问“抓住Throwable”是否可以,或者“抓住Throwable而不做任何事情可以”?这里有很多人回答了后者,但这是一个副问题; 99%的时间你不应该“消费”或放弃异常,无论你是抓住Throwable还是IOException还是其他什么。

如果你传播例外,答案(就像许多问题的答案一样)是“它取决于”。这取决于你正在做什么与例外 - 为什么你要抓住它。

为什么要捕获Throwable的一个很好的例子是在出现任何错误时提供某种清理。例如,在JDBC中,如果在事务期间发生错误,您可能希望回滚事务:

try {
  …
} catch(final Throwable throwable) {
  connection.rollback();
  throw throwable;
}

请注意,异常不会被丢弃,而是传播。

但作为一般政策,抓住Throwable,因为你没有理由而且懒得看哪些特定的异常被抛出是糟糕的形式和坏主意。

答案 10 :(得分:0)

虽然抓住Throwable通常是不好的做法(正如这个问题上的众多答案所阐明的那样),捕获Throwable的场景非常常见。让我解释一下我在工作中使用的一个这样的案例,并举一个简化的例子。

考虑一种执行添加两个数字的方法,并且在成功添加之后,它会向某些人发送电子邮件警报。假设返回的数字很重要并且由调用方法使用。

public Integer addNumbers(Integer a, Integer b) {
    Integer c = a + b;          //This will throw a NullPointerException if either 
                                //a or b are set to a null value by the
                                //calling method
    successfulAdditionAlert(c);
    return c;
}

private void successfulAdditionAlert(Integer c) {
    try {
        //Code here to read configurations and send email alerts.
    } catch (Throwable e) {
        //Code to log any exception that occurs during email dispatch
    }
}

发送电子邮件警报的代码会读取大量系统配置,因此可能会从该代码块中抛出各种异常。但是我们不希望在警报调度期间遇到任何异常传播到调用方法,因为该方法只关心它提供的两个Integer值的总和。因此,发送电子邮件警报的代码放在try-catch块中,其中捕获Throwable并且仅记录任何异常,允许其余流程继续。

答案 11 :(得分:0)

如果我们使用 throwable ,那么它也涵盖错误,就是这样。

示例

    public class ExceptionTest {
/**
 * @param args
 */
public static void m1() {
    int i = 10;
    int j = 0;
    try {
        int k = i / j;
        System.out.println(k);
    } catch (Throwable th) {
        th.printStackTrace();
    }
}

public static void main(String[] args) {
    m1();
}

}

输出

java.lang.ArithmeticException: / by zero
at com.infy.test.ExceptionTest.m1(ExceptionTest.java:12)
at com.infy.test.ExceptionTest.main(ExceptionTest.java:25)

答案 12 :(得分:0)

Throwable是所有错误和异常的超类。 如果在catch子句中使用Throwable,它不仅会捕获所有异常,还会捕获所有错误。 JVM抛出错误以指示应用程序无意处理的严重问题。典型的例子是OutOfMemoryError或StackOverflowError。两者都是由应用程序无法控制的情况引起的,无法处理。所以你不应该抓住Throwables,除非你非常有信心它只会成为Throwable中的一个例外。

答案 13 :(得分:0)

通常来说,您想避免捕获Error,但是我可以想到(至少)两种特定的情况:

  • 您要关闭应用程序以响应错误,尤其是AssertionError,否则它是无害的。
  • 您是否要实现类似于ExecutorService.submit()的线程池机制,该机制要求您将异常转发回用户,以便他们可以处理。

答案 14 :(得分:0)

一个更加差异化的答案是:取决于。

异常和错误之间的区别在于,异常是必须预期的状态,而错误是意外的状态,通常是致命的。错误通常无法从中恢复,并且需要重置程序的主要部分甚至整个JVM。

捕获异常是您应始终执行的操作,以处理可能发生的状态,这就是为什么它由JVM强制执行的原因。即打开文件会导致FileNotFoundException,调用Web资源会导致TimeoutException,依此类推。您的代码需要做好准备以处理这些情况,因为它们通常会发生。如何处理这些取决于您自己,无需从所有内容中恢复,但是您的应用程序不应仅因为Web服务器花费了更长的时间才能引导回到桌面。

仅在确实有必要时才应执行捕获错误。通常,除非有充分的理由,否则您无法从错误中恢复,也不应尝试这样做。捕获错误的原因是关闭否则将保持打开状态的关键资源,或者如果您是i.E.有一个运行插件的服务器,该服务器可以停止或重新启动导致错误的插件。其他原因是记录其他信息,这些信息可能有助于以后调试该错误,在这种情况下,您当然应该重新抛出该错误以确保应用程序正确终止。

经验法则::除非您有捕获错误的重要原因,否则不要。

因此,仅在非常重要的情况下使用catch (Throwable t),否则请坚持使用catch (Exception e)