赶上Throwable是一个坏习惯吗?


Answers:


104

您需要尽可能具体。否则,无法预料的错误可能会以这种方式消失。

此外,Throwable掩护Error也是如此,这通常是没有回报的。您不想抓住/处理该问题,而是希望您的程序立即死亡,以便可以正确修复它。


38
在某些情况下,捕获错误并继续是适当的。例:在servlet中,如果由于某个特定的请求碰巧占用了所有内存而造成了OutOfMemoryError,则您可以尝试继续,因为处理请求后对象将是GC。断言错误也是如此。您不关闭应用程序是因为请求中出现了问题。
gawi

7
您如何知道分配的是什么,以及在OOME之前没有分配的是什么?一旦掌握了所有赌注,即使是在Tomcat或JBoss之类的J2EE容器中也是如此。
13年

10
我们遇到了NoSuchMethodError,幸运的是,由于服务器在部署后两周内发生,因此无法通过关闭服务器来吸引所有客户。即。我们始终抓着Throwable并竭尽全力处理错误并将错误发送给客户。毕竟,有许多类型的错误可以恢复,因为它可能只影响1000个客户中的1个。
迪恩·希勒

10
您希望您的程序立即死亡,以便可以正确修复它 ” =>如果您的程序死亡,您如何知道发生了什么?捕获Throwable / Error来记录问题是一件合理的事情……
assylias 2015年

3
@assylias一个独立的应用程序将向stderr引发致命错误。
菲利普·怀特豪斯

36

这是一个坏主意。实际上,即使捕获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);
 }

4
+1表示即使捕获Exception也没有好处。至少有一个需要特别注意的ThreadInterruptedException(简而言之-在捕获它之后,您必须将线程的中断状态设置回'true')
Kirill Gamazkov

我知道这只是为了说明您的单词,但我认为您可以使用正则表达式检查用户输入的内容是否为字母数字或所需的格式,并且不必每次都使用try catch。
amdev '16

我应该如何知道如果没有捕获到错误/可抛出错误?我没有在日志中看到任何东西。Java EE应用程序。直到我添加此捕获,才花了几天的时间才不知道问题出在哪里。
菲利普·雷哥

2
我认为第二种选择是不好的做法。想象10个链接的呼叫,并记录并重新抛出。如果您查看日志文件,您将不会高兴。记录了10次异常,使日志很难阅读。恕我直言,要做的更好throw new Exception("Some additional info, eg. userId " + userId, e);。这将被记录为一个很好的异常,有10个原因。
PetrÚjezdský19年

21

另请注意,当您抓到时Throwable,您还可以抓到InterruptedException其中需要特殊对待的东西。有关更多详细信息,请参见处理InterruptedException

如果您只想捕获未检查的异常,则也可以考虑使用这种模式

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

这样,当您修改代码并添加可能引发检查异常的方法调用时,编译器会提醒您这一点,然后您可以决定在这种情况下该怎么做。


14

直接来自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

13

如果绝对不能从方法中产生异常冒泡,这不是一个坏习惯。

如果您确实无法处理该异常,那么这是一个不好的做法。将“抛出”添加到方法签名中比仅捕获并重新抛出更好,或者更糟的是将其包装在RuntimeException中并重新抛出。


10
完全同意-存在处理所有Throwable实例的绝对合法案例-例如自定义异常日志记录。
Yuriy Nakonechnyy 2014年

9

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

但是,在这种情况下,最好仅指定库抛出的特定错误,而不是指定所有Throwable。


12
还是使用更好的书面图书馆?
Raedwald 2013年

6
确实,如果您有选择;-)
DNA

多数民众赞成在捕捉可抛物并重新抛掷它们时遇到的最大问题。对于堆栈中所有上游的方法,这确实是一个不可能的接口。他们要么必须处理一个可抛出的签名,要么要拥有一个别人将不得不处理的不可用的可抛出签名。
Andrew Norman

6

Throwable是所有可以抛出的类的基类(不仅是异常)。如果捕获OutOfMemoryError或KernelError,您几乎无能为力(请参阅何时捕获java.lang.Error?

捕获异常应该足够了。


5

这取决于您的逻辑,或更具体地取决于您的选择/可能性。如果有任何特定的例外情况,您可以采取有意义的方式进行处理,则可以先捕获并执行。

如果没有,并且您确定将对所有异常和错误(例如,退出并显示错误消息)执行相同的操作,那么捕获可抛出对象不是问题。

通常情况下,第一种情况成立,您不会抓到可掷物。但是仍然有很多情况下捕获它可以正常工作。


4

尽管它被描述为一种非常不好的做法,但有时您可能会发现极少数情况,它不仅有用,而且具有强制性。这是两个例子。

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

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


1
在这些链接中看到了代码。可扔不仅是被抓住的!Throwable之前也捕获了其他异常。
developer1011

@ developer101当然,但是他们确实抓住了throwable,这就是这个问题的原因
Alireza Fattahi

4

这个问题有点含糊。您是在问“可以捕捉Throwable”还是“可以捕捉Throwable并且不做任何事情”?这里有很多人回答了后者,但这是附带问题。的99%的时间你不应该“消费”或放弃例外,无论你是赶上ThrowableIOException或什么的。

如果传播异常,答案(就像回答许多问题一样)是“取决于”。这取决于您对异常的处理方式-为什么要捕获它。

为什么要捕获的一个很好的示例Throwable是在出现任何错误时进行某种清理。例如在JDBC中,如果在事务期间发生错误,则需要回滚事务:

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

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

但是作为一般策略,Throwable由于您没有理由并且太懒惰而无法看到正在抛出哪些特定异常,因此捕获是不好的形式和一个坏主意。


1

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

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

0

如果我们使用throwable,那么它也涵盖了Error,仅此而已。

例。

    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)

0

Throwable是所有错误和异常的超类。如果在catch子句中使用Throwable,它将不仅捕获所有异常,还将捕获所有错误。JVM抛出错误以指示严重的问题,这些问题不应该由应用程序处理。典型的例子是OutOfMemoryError或StackOverflowError。两者都是由应用程序无法控制的情况引起的,无法处理。因此,除非您有足够的信心确保Throwable内只有例外,否则您不应捕获Throwables。


-1

尽管捕获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被捕获,并且仅记录任何异常,从而使其余流程得以继续。


我会尝试通过使用专门用于发送电子邮件的工作(带有队列)的线程来避免这种情况。
2015年

我建议这仍然是错误的代码。一定要抓住Exception,但不能Throwable
安德鲁夫
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.