抓住是一种不好的做法Throwable
吗?
例如这样的:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
这是一种不好的做法,还是我们应该尽可能具体?
抓住是一种不好的做法Throwable
吗?
例如这样的:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
这是一种不好的做法,还是我们应该尽可能具体?
这是一个坏主意。事实上,即使是捕捉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);
}
另请注意,当您捕获时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,否则您的库可能会杀死您的应用程序。
但是,在这些情况下最好只指定库抛出的特定错误,而不是所有 Throwables。
Throwable 是所有可以抛出的类的基类(不仅是异常)。如果您捕获 OutOfMemoryError 或 KernelError,您几乎无能为力(请参阅何时捕获 java.lang.Error?)
捕获异常就足够了。
这个问题有点含糊;您是在问“可以捕获Throwable
”还是“可以捕获 aThrowable
而什么也不做”?这里有很多人回答后者,但这是一个次要问题;99% 的时间你不应该“消耗”或丢弃异常,无论你是在捕捉Throwable
还是IOException
什么。
如果您传播异常,则答案(就像许多问题的答案一样)是“视情况而定”。这取决于您对异常的处理——为什么要捕获它。
一个很好的例子说明你为什么要捕获Throwable
是在出现任何错误时提供某种清理。例如在 JDBC 中,如果在事务期间发生错误,您可能希望回滚事务:
try {
…
} catch(final Throwable throwable) {
connection.rollback();
throw throwable;
}
请注意,异常不会被丢弃,而是会传播。
但作为一般策略,Throwable
因为您没有理由并且懒得查看正在抛出哪些特定异常而进行捕获是一种糟糕的形式和一个坏主意。
这取决于您的逻辑或更具体到您的选项/可能性。如果有任何特定的异常你可以用有意义的方式做出反应,你可以先抓住它并这样做。
如果没有,并且您确定您将对所有异常和错误执行相同的操作(例如退出并显示错误消息),那么捕获可抛出对象是没有问题的。
通常第一种情况成立,你不会抓住投掷物。但是仍然有很多情况可以很好地捕捉它。
尽管它被描述为一种非常糟糕的做法,但您有时可能会发现它不仅有用而且是强制性的。这里有两个例子。
在 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
一般来说,您希望避免捕获Error
s 但我可以想到(至少)两种适合这样做的特定情况:
AssertionError
是在其他方面无害的错误。Throwable 是所有错误和异常的超类。如果在 catch 子句中使用 Throwable,它不仅会捕获所有异常,还会捕获所有错误。JVM 抛出错误以指示不打算由应用程序处理的严重问题。典型的例子是 OutOfMemoryError 或 StackOverflowError。两者都是由应用程序无法控制且无法处理的情况引起的。所以你不应该捕获 Throwables,除非你非常有信心它只会是 Throwable 中的一个异常。
如果我们使用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)
一个更有区别的答案是:视情况而定。
Exception 和 Error 之间的区别在于,Exception 是一种必须预期的状态,而 Error 是一种意外状态,通常是致命的。错误通常无法恢复,需要重置程序的主要部分甚至整个 JVM。
捕获异常是您应该始终执行的处理可能发生的状态的事情,这就是 JVM 强制执行它的原因。IE打开文件会导致FileNotFoundException,调用web资源会导致TimeoutException,等等。您的代码需要准备好处理这些情况,因为它们通常会发生。您如何处理这些取决于您,无需从所有内容中恢复,但您的应用程序不应仅仅因为 Web 服务器需要更长的时间来响应而启动回桌面。
捕获错误是您应该仅在确实有必要时才应该做的事情。通常,您无法从错误中恢复并且不应尝试恢复,除非您有充分的理由这样做。捕获错误的原因是关闭本来会保持打开状态的关键资源,或者如果您的 iE 有一个运行插件的服务器,那么它可以停止或重新启动导致错误的插件。其他原因是记录可能有助于稍后调试该错误的附加信息,在这种情况下,您当然应该重新抛出它以确保应用程序正确终止。
经验法则:除非你有重要的理由来捕捉错误,否则不要。
因此catch (Throwable t)
只在非常重要的情况下使用,否则坚持catch (Exception e)