捕获throwable并处理特定异常

rhe*_*ser 23 java exception-handling exception

好的,我知道抓住throwable不是一个好主意:

    try {
         // Some code
    } catch(Throwable e) { // Not cool!
        // handle the exception
    }
Run Code Online (Sandbox Code Playgroud)

但最近我正在阅读一个开源代码,我看到了这段有趣的代码(至少对我而言):

    try {
        // Some Code
    } catch (Throwable ex){
        response = handleException(ex, resource);
    }

    private handleException(Throwable t, String resource) {
        if (t instanceof SQLEXception) {
               // Some code
        } else if (t instanceof IllegalArgumentException) {
               //some code
        } //so on and so forth
    }
Run Code Online (Sandbox Code Playgroud)

这似乎不是那么糟糕?这种方法有什么问题?

Flo*_*etz 19

你不应该抓住Throwable的原因有多种.首先,它Throwable包括Errors - 并且如果其中一个出现,通常没有多少应用程序可以做.此外,Throwable减少了发现的可能性,发生了什么.你得到的只是"发生了一件坏事" - 这可能是一场灾难,也可能只是一种滋扰.

另一个方法更好但当然我仍然不会抓住Throwable,但是如果可能的话,尽量抓住更具体的异常.否则你会抓住所有东西,然后尝试找出发生了哪种坏事.你的例子可以写成......

try {
    ...
} catch (SQLEXception ex){
    response = ... ;
} catch (IllegalArgumentException ex){
    response = ...;
}
Run Code Online (Sandbox Code Playgroud)

...这将减少if ( ... instanceof ... )块的数量(这只是因为作者首先决定在一个大桶中捕获所有东西而需要).事实上throws Throwable,当然,你没有太多选择.

  • 不,我的意思是框架本身必须捕获错误并处理它.不幸的是,Java的作者没有区分可恢复和不可恢复的错误,所以有时(很少)捕获`Throwable`是唯一合理的选择. (3认同)
  • 问题是"错误"非常容易从中恢复.如果一个框架试图加载一个类并且它失败了,那么事情仍然可以. (2认同)
  • 喔好吧。是的,您就在那里,但是对于非常低级的用例,这些是非常非常具体的情况。这就是为什么我写“通常”应用程序不能做太多事情的原因。如果您是 Spring 框架并遇到错误,那么您可能可以做些什么。如果您是一个 HelloWorld 应用程序,那么您最好的选择就是在有人修复您的错误时简单地休假 ;-) (2认同)

CKi*_*ing 18

当你说捕捉Throwable不是一个好主意时,你是对的.但是,你在问题中提出的代码并没有Throwable以邪恶的方式进行,但让我们稍后再讨论.目前,您在问题中提供的代码有几个优点:

1.可读性

如果仔细查看代码,您会注意到即使catch块正在捕获a Throwable,该handleException方法也会检查抛出的异常类型,并可能根据异常类型采取不同的操作.

您问题中提供的代码与以下内容同义:

try {
    doSomething();
} catch (SQLEXception ex){
    response = handleException(resource);
} catch(IllegalArgumentException ex) {
    response = handleException(resource);
} catch(Throwable ex) {
    response = handleException(resource);
}
Run Code Online (Sandbox Code Playgroud)

即使你只需要捕获10多个例外,这段代码很容易占用大量代码,而multi-catch构造也不会让代码变得更清晰.您在问题中提供的代码只是委托catch给另一个方法,以使实际工作方法更具可读性.

2.可重用性

handleRequest方法的代码可以很容易地修改并放在实用程序类中,并在整个应用程序中访问以处理Exceptions和Errors.您甚至可以将方法提取为两种private方法; 一,处理Exception,另一种处理Error,并有handleException,需要一个方法Throwable进一步委托给这些方法的调用.

3.可维护性

如果您决定要更改SQLException在应用程序中记录s 的方式,则必须在单个位置进行此更改,而不是访问每个抛出的类中的每个方法SQLException.

所以抓住Throwable一个坏主意?

您在问题中提供的代码与Throwable单独捕获的代码并不完全相同.以下代码是一个很大的禁忌:

try {
   doSomething();
} catch(Throwable e) {
    //log, rethrow or take some action
}
Run Code Online (Sandbox Code Playgroud)

你应该尽可能地抓住ThrowableException远离catch链条.

最后但并非最不重要的是,请记住,您在问题中提供的代码是框架代码,并且框架仍然可以从中恢复某些错误.有关更好的解释,请参见何时捕获java.lang.Error.

  • @maraca请记住,这是框架代码.Fameworks希望捕获可恢复的错误.(链接错误是一个主要的例子).话虽如此,我同意他们使用的方法名称在这里并不是一个好的选择,但这与实际问题无关. (2认同)
  • @maraca它们捕获的大多数异常都是`RuntimeException`的子类.`RuntimeException不需要遵循catch或指定需求,因此框架人员不能保证他们的API用户将处理这些异常.它们可能可能将这些异常包装到已检查的异常中并抛出它们,或者它们可能代表它们处理这些异常.最重要的是,没有一点声明一个方法`抛出RuntimeException`在这种情况下IMO. (2认同)

biz*_*lop 9

抓住Throwable懒惰是一个坏主意.

这在try-multi-catch介绍之前特别诱人.

try {
   ...
} catch (SomeException e) {
   //do something
} catch (OtherException e) {
   //do the same thing
} ...
Run Code Online (Sandbox Code Playgroud)

重复捕获块是繁琐和冗长的,所以有些人决定抓住ExceptionThrowable完成它.这是应该避免的,因为:

  1. 这使得你很难理解你想要做的事情.
  2. 你最终可能会抓到很多你无法处理的东西.
  3. 如果你完全吞下Throwable捕获区,你应该得到奖金惩罚.(而且我们都看过这样做的代码...... :))

但是Throwable在绝对必要的情况下捕捉它是好的.

什么时候需要?非常稀有.在框架式代码中有各种场景(动态加载外部类是最明显的一种),在独立应用程序中,典型的例子是在退出之前尝试显示/记录某种错误消息.(请记住,尝试可能会失败,所以你不想在那里放任何关键的东西.)

根据经验,如果你无法对异常/错误做任何事情,你根本不应该抓住它.