捕获 Throwable 是一种不好的做法吗?
-
08-09-2020 - |
题
抓住这是一个不好的做法吗 Throwable
?
例如这样的事情:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
这是一种不好的做法还是我们应该尽可能具体?
解决方案
您需要尽可能具体。否则无法预料的错误可能会以这种方式蠕动。
除了, Throwable
覆盖Error
其他提示
这是一个坏主意。事实上,甚至捕获的世代植物都是一个坏主意。让我们考虑一个例子:
try {
inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(Throwable e) {
inputNumber = 10; //Default, user did not enter valid number
}
.
现在,让我们说getUserInput()块有一段时间,另一个线程以最糟糕的方式停止您的线程(它调用thread.stop())。您的Catch块将捕获Exception
错误。这是超级糟糕的。捕获异常后的代码的行为在很大程度上是未定义的。
捕捉异常发生类似的问题。可能是生成的因子因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 - 使用最终块,以便您不必记得Rethrow:
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
}
这样,当您修改代码并添加可以引发已检查异常的方法调用时,编译器会提醒您,然后您可以决定针对这种情况做什么。
直接从错误类的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并重新投掷。
捕获量会有有时需要如果您使用过热情地抛出错误,否则您的图书馆可能会杀死您的应用程序。
但是,在这些情况下,最好只指定库抛出的特定错误,而不是所有thllables。throwable是所有类的基类,而不是可以抛出(不仅例外)。如果你抓住了一个OutofMemoryError或KernelEror,你可以做些什么(参见捕获java。lang.error?)
捕捉异常应该足够。
取决于您的逻辑或更具体地对您的选项/可能性。如果有任何特定的例外,您可以以有意义的方式反应,您可以先捕获它并执行此操作。
如果没有,你确定你会为所有例外和错误做同样的事情(例如用错误消息退出),而不是捕获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
问题有点模糊;你是在问“可以抓住吗 Throwable
”,或者“可以抓到吗? Throwable
并且什么也不做”?这里很多人都回答了后者,但这是一个次要问题;99% 的情况下,无论您是否正在捕获异常,您都不应该“消耗”或丢弃该异常 Throwable
或者 IOException
管他呢。
如果您传播异常,答案(就像许多问题的答案一样)是“视情况而定”。这取决于你对异常做了什么——为什么你要捕获它。
一个很好的例子来说明为什么你想要抓住 Throwable
是在出现任何错误时提供某种清理。例如在 JDBC 中,如果事务期间发生错误,您可能希望回滚事务:
try {
…
} catch(final Throwable throwable) {
connection.rollback();
throw throwable;
}
请注意,异常并未被丢弃,而是被传播。
但作为一般政策, Throwable
因为您没有理由并且懒得查看抛出了哪些特定异常,这是糟糕的形式和坏主意。
一般来说,您想要避免捕获生成的捕获量,但我可以想到(至少)两个特定的情况,在那里适合这样做:
- 要响应错误,尤其是无害的错误,尤其是无害的。
- 您是否在实现类似于 executorservice.submit(),要求您将例外转发回用户,以便它们可以处理它。
如果我们使用 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)
. throwable是所有错误和卓越的超类。 如果在Catch子句中使用ROLLABLE,它不仅会捕获所有异常,它也会捕获所有错误。JVM抛出错误以指示不打算通过应用程序处理的严重问题。典型的例子是outofmemoryerror或stackoverfloweror。两者都是由申请控制之外的情况引起的,无法处理。所以你不应该抓住thlowables,除非你的相当自信,它只是一个例外居住在洛克里面。
虽然捕获 Throwable 通常是不好的做法(正如该问题的众多答案所阐明的那样),但捕获 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
被捕获并且仅记录任何异常,从而允许流程的其余部分继续。