【问题标题】:Catching throwable and handling specific exceptions捕获可抛出并处理特定异常
【发布时间】:2015-10-13 15:14:19
【问题描述】:

好的,我知道捕获 throwable 不是一个好主意:

    try {
         // Some code
    } catch(Throwable e) { // Not cool!
        // handle the exception
    }

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

    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
    }

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

【问题讨论】:

  • 有时,尤其是在框架中,除了捕获Throwable,您别无选择。在应用程序级代码中这仍然不是一个好主意,除非您想要某种“最后的处理程序”。
  • 您是否有相关开源代码的链接(例如 GitHub 上的源文件)?我可以想到几个用例,根据具体情况,这实际上可能是一个很好的策略。

标签: java exception exception-handling


【解决方案1】:

您不应该捕获 Throwable 有多种原因。首先,Throwable 包括 Errors - 如果出现其中之一,应用程序通常无能为力。 Throwable 也减少了你发现发生了什么的机会。你得到的只是“发生了一些不好的事情”——这可能是一场灾难,也可能只是一种滋扰。

另一种方法更好,但当然我仍然不会捕获 Throwable,但如果可能的话,尝试捕获更具体的异常。否则,您会抓住一切,然后尝试找出发生了哪种坏事。你的例子可以写成...

try {
    ...
} catch (SQLEXception ex){
    response = ... ;
} catch (IllegalArgumentException ex){
    response = ...;
}

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

【讨论】:

  • 问题是Errors 非常容易恢复。如果一个框架尝试加载一个类但它失败了,事情还是可以的。
  • 在这种情况下,框架不应产生错误,而是产生更有意义的异常。当然,事实并非总是如此。
  • 不,我的意思是框架本身必须捕获错误并处理它。不幸的是,Java 的作者没有区分可恢复和不可恢复的错误,所以有时(很少)捕获Throwable 是唯一明智的选择。
  • 啊,好的。是的,你就在那里,但是对于非常低级的用例,这些是非常非常具体的情况。这就是为什么我写“通常”应用程序不能做太多事情的原因。如果您是 Spring 框架并遇到错误,您可能可以做一些事情。如果您是 HelloWorld 应用程序,那么您最好的选择就是在有人修复您的错误时简单地休假;-)
  • @FlorianSchaetz 如果您是 Web 服务器并且在处理请求时遇到错误,您希望记录它并仅中止该请求,而不关闭服务器。 (除非每个请求都抛出错误)
【解决方案2】:

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

1.可读性

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

您问题中提供的代码相当于说:

try {
    doSomething();
} catch (SQLEXception ex){
    response = handleException(resource);
} catch(IllegalArgumentException ex) {
    response = handleException(resource);
} catch(Throwable ex) {
    response = handleException(resource);
}

即使您只需要捕获 10 多个异常,此代码也很容易占用大量代码行,而且多捕获构造不会使代码变得更简洁。您在问题中提供的代码只是将catch 委托给另一种方法,以使执行该工作的实际方法更具可读性。

2。可重用性

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

3.可维护性

如果您决定要更改在应用程序中记录SQLExceptions 的方式,则必须在一个地方进行此更改,而不是访问每个类中引发SQLException 的每个方法。

那么捕捉Throwable 是个坏主意吗?

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

try {
   doSomething();
} catch(Throwable e) {
    //log, rethrow or take some action
}

您应该在catch 链中尽可能远地捕获ThrowableException

最后但并非最不重要的一点是,请记住,您在问题中提供的代码是框架的代码,并且框架仍然可以从某些错误中恢复。请参阅When to catch java.lang.Error 以获得更好的解释。

【讨论】:

  • 你为什么要使用Throwable 而不仅仅是Exception?该方法必须被称为 handleThrowable...
  • @maraca 请记住,这是框架代码。 Fameworks 希望捕获可恢复的错误。 (链接错误是一个典型的例子)。话虽如此,我同意他们使用的方法名称在这里并不是一个好的选择,但这与实际问题无关。
  • 谢谢,我明白了。但我认为这是一个设计问题,无法想出一个好的方法。他们可能应该只在正确的位置使用 throws 和 handle,而不是捕获所有内容并调用此方法。
  • @maraca 他们捕获的大多数异常都是RuntimeException 的子类。 RuntimeExceptions 不需要遵循 catch 或指定要求,因此框架人员无法保证其 API 的用户将处理这些异常。他们可能会将这些异常包装到已检查的异常中并抛出它们,或者他们可能会代表他们处理这些异常。底线,在这种情况下,没有必要声明方法 throws RuntimeException IMO。
【解决方案3】:

因为懒惰而抓住Throwables 是个坏主意。

在引入try-multi-catch 之前,这特别诱人。

try {
   ...
} catch (SomeException e) {
   //do something
} catch (OtherException e) {
   //do the same thing
} ...

重复 catch 块既乏味又冗长,因此有些人决定只捕获 ExceptionThrowable 并完成它。这是应该避免的,因为:

  1. 这让您很难遵循您正在尝试做的事情。
  2. 您最终可能会遇到很多您无法处理的问题。
  3. 如果你完全吞下 catch 块中的 Throwable,你应该得到额外的惩罚。 (而且我们都见过这样做的代码...:))

但是在绝对必要的时候捕捉Throwables 是可以的。

什么时候需要?非常稀有。在框架风格的代码中有各种场景(动态加载外部类是最明显的一种),在独立应用程序中,一个典型的例子是在退出之前尝试显示/记录某种错误消息。 (请记住,尝试可能会失败,因此您不想在其中放置任何关键内容。)

根据经验,如果您对异常/错误无能为力,则根本不应该捕获它。

【讨论】:

    【解决方案4】:

    首先,捕获 Throwable 会使您的应用程序相当不透明。您应该尽可能明确地捕获异常,以便在异常情况下实现良好的可追溯性。

    让我们看一下方法handleException(...),看看这种方法会出现的一些问题:

    • 您捕获 Throwable 但您只处理异常,如果出现例如抛出类型错误的 OutOfMemoryError? - 我看到不好的事情发生...
    • 关于使用 instanceof 的良好面向对象编程打破了开闭原则并使代码更改(例如添加新异常)变得非常混乱。

    在我看来,catch-blocks 正是为试图在 handleExceptions(...) 中覆盖的功能而制作的,所以请使用它们。

    【讨论】:

      【解决方案5】:

      Java 7 解决了一些单调乏味的问题,即用类似的处理方式多次捕获类似的异常。你绝对不应该做这个人在这里所做的事情。只需根据需要捕获适当的异常,它可能看起来很难看,但这就是 throws 的用途,将它传递给应该捕获它的方法,您不应该浪费太多代码空间。

      Check out this link for more information.

      【讨论】:

        【解决方案6】:

        只是为了提供平衡 - 我将永远有一个的地方catch (Throwable)

        public static void main(String args[]) {
            try {
                new Test().test();
            } catch (Throwable t) {
                t.printStackTrace(System.err);
            }
        }
        

        至少有些地方显示出了问题。

        【讨论】:

        • 真的吗???我从不这样做......因为它总是被打印出来,无论如何。也许它是特定于操作系统的?
        【解决方案7】:

        您发布了一个指向 Jongo 的链接,其中演示了该技术的一种可能用途:重用错误处理代码。

        假设您有一大块错误处理代码,这些代码自然会在代码的不同位置重复出现 - 例如,Jongo 会为某些标准错误类别生成标准响应。将错误处理代码提取到方法中可能是个好主意,这样您就可以在所有需要的地方重复使用它。

        但是,这并不是说 Jongo 的代码没有问题。

        捕捉Throwable(而不是使用multicatch)仍然是可疑的,因为你可能会捕捉到你无法处理的Errors(你确定你的意思是抓住ThreadDeath?)。在这种情况下,如果你一定要抓住Throwable,最好是“抓住并释放”(即重新抛出任何你不想抓住的东西)。 Jongo 不会这样做。

        【讨论】:

          【解决方案8】:

          使用大网有两种有效的用途:

          • 如果您要统一处理所有事情,例如用于记录/报告的顶级捕获,然后可能会立即退出。

          • 为减少重复,将所有处理导出到自己的方法中。
            捕捉最衍生的共同祖先以避免额外工作并提高清晰度。
            DRY 是一个重要的设计原则。

          在这两种情况下,除非您预料到该异常并完全处理它,否则请重新抛出。

          【讨论】:

            【解决方案9】:

            您始终可以捕获不同类型的异常,并根据您遇到的异常类型执行一些操作。

            这是一个例子

                      try{
            
                          //do something that could throw an exception
            
                         }catch (ConnectException e) {
                            //do something related to connection
            
            
                        } catch (InvalidAttributeValueException e) {
                            // do anything related to invalid attribute exception
            
                        } catch (NullPointerException e) {
            
                            // do something if a null if obtained
                        }
            
                        catch (Exception e) {
                        // any other exception that is not handled can be catch here, handle it here
            
                        }
                        finally{
                      //perform the final operatin like closing the connections etc.
                         }
            

            【讨论】:

              猜你喜欢
              • 2015-07-14
              • 2023-03-18
              • 2012-04-26
              • 2010-11-06
              • 2018-03-23
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-10-19
              相关资源
              最近更新 更多