Answers:
您需要尽可能具体。否则,无法预料的错误可能会以这种方式消失。
此外,Throwable
掩护Error
也是如此,这通常是没有回报的。您不想抓住/处理该问题,而是希望您的程序立即死亡,以便可以正确修复它。
这是一个坏主意。实际上,即使捕获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);
}
throw new Exception("Some additional info, eg. userId " + userId, e);
。这将被记录为一个很好的异常,有10个原因。
另请注意,当您抓到时Throwable
,您还可以抓到InterruptedException
其中需要特殊对待的东西。有关更多详细信息,请参见处理InterruptedException。
如果您只想捕获未检查的异常,则也可以考虑使用这种模式
try {
...
} catch (RuntimeException exception) {
//do something
} catch (Error error) {
//do something
}
这样,当您修改代码并添加可能引发检查异常的方法调用时,编译器会提醒您这一点,然后您可以决定在这种情况下该怎么做。
直接来自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
如果绝对不能从方法中产生异常冒泡,这不是一个坏习惯。
如果您确实无法处理该异常,那么这是一个不好的做法。将“抛出”添加到方法签名中比仅捕获并重新抛出更好,或者更糟的是将其包装在RuntimeException中并重新抛出。
Throwable
实例的绝对合法案例-例如自定义异常日志记录。
如果您使用的库过分热情地抛出错误,则有时需要捕获Throwable,否则您的库可能会杀死您的应用程序。
但是,在这种情况下,最好仅指定库抛出的特定错误,而不是指定所有Throwable。
Throwable是所有可以抛出的类的基类(不仅是异常)。如果捕获OutOfMemoryError或KernelError,您几乎无能为力(请参阅何时捕获java.lang.Error?)
捕获异常应该足够了。
尽管它被描述为一种非常不好的做法,但有时您可能会发现极少数情况,它不仅有用,而且具有强制性。这是两个例子。
在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
确实已缓存和处理了。例如这里是一个搜索tomcat
,struts2
并primefaces
:
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
throwable
,这就是这个问题的原因
这个问题有点含糊。您是在问“可以捕捉Throwable
”还是“可以捕捉Throwable
并且不做任何事情”?这里有很多人回答了后者,但这是附带问题。的99%的时间你不应该“消费”或放弃例外,无论你是赶上Throwable
或IOException
或什么的。
如果传播异常,答案(就像回答许多问题一样)是“取决于”。这取决于您对异常的处理方式-为什么要捕获它。
为什么要捕获的一个很好的示例Throwable
是在出现任何错误时进行某种清理。例如在JDBC中,如果在事务期间发生错误,则需要回滚事务:
try {
…
} catch(final Throwable throwable) {
connection.rollback();
throw throwable;
}
请注意,异常不会被丢弃,而是会传播。
但是作为一般策略,Throwable
由于您没有理由并且太懒惰而无法看到正在抛出哪些特定异常,因此捕获是不好的形式和一个坏主意。
一般来说,您想避免捕获Error
s,但是我可以想到(至少)两种特定的情况:
AssertionError
是在其他情况下无害的情况。如果我们使用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)
Throwable是所有错误和异常的超类。如果在catch子句中使用Throwable,它将不仅捕获所有异常,还将捕获所有错误。JVM抛出错误以指示严重的问题,这些问题不应该由应用程序处理。典型的例子是OutOfMemoryError或StackOverflowError。两者都是由应用程序无法控制的情况引起的,无法处理。因此,除非您有足够的信心确保Throwable内只有例外,否则您不应捕获Throwables。
尽管捕获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
被捕获,并且仅记录任何异常,从而使其余流程得以继续。
Exception
,但不能Throwable
。