【问题标题】:Throw a long list of exceptions vs throw an Exception vs throw custom exception?抛出一长串异常与抛出异常与抛出自定义异常?
【发布时间】:2011-05-30 01:11:17
【问题描述】:

我有一个使用 API 的两种方法的应用程序。这两种方法都各自抛出超过五个异常。所以,如果我只是添加一个 throws 声明,那么它就会变成一个超过十个的列表。 (我的方法无法处理十个异常中的任何一个

我已经读过抛出一长串异常是一种不好的做法。抛出(伞)异常也是一种不好的做法。所以我该怎么做?

  1. 添加try catch块,在catch块中记录退出?
  2. 创建自定义异常类,包装每个异常并抛出自定义异常?
  3. 为所有异常添加 throws 声明?
  4. 抛出异常?
  5. 添加一个try catch块,并在catch块中抛出一个RuntimeException? (当前方法)

编辑:添加了选项 5。

【问题讨论】:

  • 抛出的异常是特定家族的成员吗?例如IOException

标签: java exception-handling


【解决方案1】:

“2.创建自定义异常类”,但不是全部。将异常包装在逻辑组中。例如,您可以使用 XmlWritingExceptionPaymentGatewayExceptionDataAccessException,它们根据场景包装不同的异常。

甚至可以(并且首选)将相同的异常包装在不同的包装器中。例如,如果由于通信问题导致支付失败,您可以将IOException 包装在PaymentGatewayException 中,但如果在使用xml 的某些I/O 操作期间付款失败,则可以在XmlWritingException 中包装。所有这些例子都是假设的,但你明白了。

最重要的是 - 将原始异常设置为新异常的原因,这样它就不会丢失。

更新:事实上选项 5 很好,以防您不能期望客户端从异常中合理地恢复。更好的是 - 您创建的自定义异常可以扩展 RuntimeException。这就是spring所做的,例如,将所有与数据相关的异常包装在DataAccessException中。

【讨论】:

    【解决方案2】:

    一般来说,如果你的方法不能处理异常,它应该重新抛出它们或将它们添加到 throws 部分。在 catch 块中隐藏异常似乎是最糟糕的选择。

    【讨论】:

      【解决方案3】:

      一如既往,视情况而定。

      1. 是你的 API 吗?如果是,则更改它并让它抛出更少的异常。以某种方式处理它们。

      2. 尽量让您的程序工作得尽可能久。这意味着,如果没有配置文件,不要抛出 IOException 并退出。硬编码一些默认配置。如果没有互联网连接,请显示消息并且不要退出。

      3. 有时,可以返回一些特殊的东西(特殊情况模式和空对象模式的变体)而不是抛出异常。

      4. 考虑为一些例外情况实施观察者模式。不要抛出异常,而是通知应该显示消息或执行其他操作的人。

      5. 不要隐藏其中任何一个。记录所有异常。

      【讨论】:

        【解决方案4】:

        如果您的 API 名为 Foo,我将创建一个名为 FooAPIException 的异常。确保在 FooAPIException 中嵌入源异常。在记录和显示 FooAPIException 时,还会显示源异常的堆栈跟踪。

        例如:

        public class FooAPIException extends Exception {
            private Exception root;
        
            public FooAPIException (Exception e) {
                super(e.getMessage(),e.getErrorCode());
                root = e;
            }
        
            public Exception getRoot () {
                return root;
            }
        
        // Exception ================================================================
            public String toString () {
                StringBuffer sb = new StringBuffer();
        
                sb.append("[");
                sb.append(getErrorCode());
                sb.append("][");
                sb.append(getMessage());
                sb.append("]\n");
                sb.append("ROOT CAUSE:");
        
                Writer write = new StringWriter();
                PrintWriter pw = new PrintWriter(write);
                e.printStackTrace(pw);
                pw.close();
                try {
                    write.close();
                } catch (IOException ioe) {/**/}
                root = write.toString();
                sb.append(write);
        
                return sb.toString();
            }   
        }
        

        然后包装API:

        public class FooAPI {
            public static method (String params) throws FooAPIException {
                try {
                    RealFooAPI.method(params)
                } catch (Exception e) {
                    throw new FooAPIException(e);
                }
            }
        }
        

        【讨论】:

          【解决方案5】:

          您可以使用一些自定义异常来包装异常。

          然而,这样做的问题是,通过隐藏导致异常的原因(例如,它是IOException),您隐藏了理论上允许更高级别的代码专门捕获该异常的信息,并且处理它。在这种情况下,你不会比扔RuntimeException 更好,所以你也可以用RuntimeException 包裹它们。

          我仍然会留下那些你可以想象的合理的调用者希望专门捕获并专门声明它们,或者将它们包装成组的那些。

          【讨论】:

          • 只存储原件,如果有人想要异常,他们可以捕获一般并检查原件。这个问题的含义是他对例外并不十分感兴趣。
          • @aepryus:这就是为什么我说,如果您对异常不感兴趣,只需将它们包装在 RuntimeException 中即可。我的观点是,用一些通用的检查异常来包装它们是没有意义的。
          • 如果,OTOH,他们对原始异常感兴趣,最好离开它——写catch (SomeException e) {...比写catch (GeneralException e) { if (e.getCause() instanceof SomeException) { SomeException se = (SomeException)e.getCause(); ...容易得多
          • 我想,但我更愿意将 RuntimeExceptions 留给我认为是错误的事情。如果在我的代码中调用了 RuntimeException,那是因为它在某些地方存在错误,而不是一些需要处理的预期问题。
          • 我通常会做一类异常(MyProjectException),我真的无法处理的所有库异常都会转换成。是否将此设置为已检查或未检查的异常是个人喜好问题 - 我更喜欢它是 RuntimeException,否则整个代码库都会被throws ProjectException 乱扔,除非在我处理的级别上得到正确处理无论如何,RuntimeExceptions(错误)。
          【解决方案6】:

          如果您的代码的调用者可以想象处理一些异常,那么我会将您的方法声明为抛出它们并传递它们。如果没有希望,那么我将创建您自己的扩展(未经检查的)RuntimeException 的自定义异常,并抛出您的自定义异常,并将实际异常链接到其中。

          我通常会尽可能推迟做出退出政策的决定。退出(如果您的意思是退出进程)在代码中应该始终是可延迟的,以便您可以对其进行单元测试。

          总的来说,我对异常的看法是:

          1. 如果错误是“业务逻辑问题”并且调用者可以处理它(通过选择替代逻辑或询问用户要做什么),则抛出自定义检查异常。示例:抛出 UserDoesNotExistException 表示数据系统中不存在用户名,并让下一层要求用户创建帐户。当您使用它时,您可能想考虑是否可以以某种方式构造代码,从而实际上不需要异常。
          2. 意外的异常应该是运行时(未经检查的)异常。您希望它们将您的代码基本上保持不变,使其尽可能高(线程堆栈很小),在那里它们可以作为异常情况进行处理(通过抛出错误页面、记录错误、向操作员发送警报、自省系统状态,包括错误报告等)。除非您可以添加可用于诊断问题的实际信息,否则请勿捕获、包装和重新抛出。

          【讨论】:

            【解决方案7】:

            如果您不在该方法中处理异常,最好将它们包装到一个或多个自定义异常中并抛出它们,以便您可以在上层处理它们。

            您还可以创建自定义未检查(扩展 RuntimeException)异常并将抛出的异常包装到自定义运行时异常中。

            【讨论】:

              猜你喜欢
              • 2013-05-24
              • 2010-11-30
              • 2011-04-28
              • 2011-10-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-02-25
              相关资源
              最近更新 更多