【问题标题】:When should we create our own Java exception classes? [closed]我们什么时候应该创建自己的 Java 异常类? [关闭]
【发布时间】:2014-05-07 01:46:26
【问题描述】:

从良好的设计/实践的角度来看,我们什么时候应该创建和使用自定义 Java 异常类,而不是 Java 中已经预定义的那些?

在一些应用程序中,我看到几乎创建了自定义异常类,它们努力始终使用本机 Java 异常。另一方面,有些应用程序为(几乎)所有内容定义了自定义异常。

【问题讨论】:

    标签: java class exception api-design


    【解决方案1】:

    来自Best Practices for Exception Handling

    如果它们没有对客户端代码有用的信息,请尽量不要创建新的自定义异常。

    以下代码有什么问题?

    public class DuplicateUsernameException extends Exception {}
    

    除了指示性异常名称之外,它没有向客户端代码提供任何有用的信息。不要忘记 Java Exception 类与其他类一样,您可以在其中添加您认为客户端代码将调用的方法以获取更多信息。

    我们可以为DuplicateUsernameException添加有用的方法,例如:

    public class DuplicateUsernameException
        extends Exception {
        public DuplicateUsernameException 
            (String username){....}
        public String requestedUsername(){...}
        public String[] availableNames(){...}
    }
    

    新版本提供了两个有用的方法:requestedUsername(),返回请求的名称,availableNames(),返回与请求的用户名相似的可用用户名数组。客户端可以使用这些方法来通知请求的用户名不可用并且其他用户名可用。但如果你不打算添加额外的信息,那么只需抛出一个标准异常:

    throw new IllegalArgumentException("Username already taken");
    

    【讨论】:

    • 不要不要只扔一个Exception!这意味着您必须捕获 Exception,这反过来意味着您还捕获所有 RuntimeExceptions,因此所有 NPE 等!
    • “它没有向客户端代码提供任何有用的信息,除了指示性异常名称”:但是如果您需要进行特定的错误处理,that 可能很有用重复名称,例如提示输入不同的名称。
    • 在使用 Java 构建 REST API 服务器时,为不同类型的错误设置单独的异常不是合乎逻辑吗?这样我们就可以捕获错误类型并将其消息与适当的状态代码一起发送(我不想将 http 状态代码与我的域异常混合)?
    【解决方案2】:

    从良好的设计/实践的角度来看,我们什么时候应该创建和使用自定义 java 异常类而不是 java 中已经预定义的类?

    当现有的异常名称不能满足您的需要时。

    另一个设计关注点是扩展“好”异常类;例如,如果您引发与 I/O 相关的异常,则理想情况下应该继承 IOException;如果异常指示程序员错误,您应该继承RuntimeException(即,使您的异常未选中)。

    引发自定义异常还可以让您以更精确的方式处理异常;例如,如果你定义了FooException继承IOException,那么你可以对其进行特殊处理:

    try { ... }
    catch (FooException e) { ... } // Catch it _before_ IOException!
    catch (IOException e) { ... }
    

    此外,异常是与任何其他类一样的类,因此您可以添加自定义方法等;例如,杰克逊定义了JsonProcessingException,它继承了IOException。抓到的话可以通过.getLocation()获取解析错误的位置信息。

    【讨论】:

      【解决方案3】:

      当然,当您希望能够以编程方式处理异常时 - 即很容易为不同的异常类型创建单独的 catch 语句,即:

      try{    
        buyWidgets();
      }
      catch(AuthenticationException ex)
      {
        promptForLogin();
      }
      catch(InsufficientFundsException ex)
      {
        promptToRefillAccount();
      }
      //let other types of exceptions to propagate up the call stack
      

      关于上述是否构成不恰当地使用异常进行流控

      虽然异常比 if-else 语句更耗费 CPU(主要是由于构建堆栈跟踪的成本),但成本是相对的,应该在特定用例的上下文中进行评估。并非每段代码都需要快速实现网络规模,有些人发现阅读和测试条件更加麻烦。例如,几乎所有事务管理器都使用异常来实现 commit-rollback-retry 习惯用法。 (尝试在不捕获异常的情况下编写事务重试方面)

      另外,应该坚持关注点分离原则:不是每段代码都需要处理所有可能的情况。购买小部件时未登录是否属于例外情况,实际上取决于应用程序和应用程序代码库中的特定位置。例如,您可以拥有一个为登录用户提供操作的服务。该服务中的方法处理身份验证没有任何意义 - 相反,这些方法会期望调用链中的代码更早地确保用户经过身份验证,因此如果不是这样,则简单地抛出异常。因此,对于那些未登录的方法 IS 是一个例外情况。

      【讨论】:

      【解决方案4】:

      您在使用自己的异常时这样做会将 value 添加到您的代码库中。

      就是这么简单。值可能是:

      • 在更高级别上捕获可能会更容易,例如当所有异常都具有共同的基类型时
      • 您的异常携带附加数据(我们在自己的异常中包含 NLS 键,以便更高层知道如何在考虑 I18N 的情况下向人类用户提供消息)

      【讨论】:

      • 我认为举个例子会更有帮助;)
      • @GhostCat 示例请...
      • 我不得不承认:我举了一个例子。展示我们如何准确地使用异常来携带 nls 信息有什么帮助?我给出的例子概述了为什么异常可以在概念上得到丰富。
      【解决方案5】:

      如果我需要传达更多信息而不仅仅是错误消息,我通常会创建自定义异常。

      例如特定的错误代码或实际值与预期值。这些也可以有自己的 getter,因此您可以通过编程方式检索这些字段,而无需解析 String 消息,如果您更改消息的文本可能会中断。 (或者翻译成其他语言)

      如果您创建自己的异常,我建议您扩展常见的 JDK 内置异常,以便您的 API 可以说 throws IOException,但它实际上会抛出 MycustomIOException。这样一来,您的 API 用户就不必知道您自己的自定义版本,除非他们愿意。

      【讨论】:

        【解决方案6】:

        在您的应用程序中拥有自定义异常是一件好事,一个可能是应用程序的顶级自定义异常,其他的可能是模块/包级别的异常。如果您在应用程序中有一些特定的功能/操作,并且您需要让用户知道操作期间是否发生任何异常,那么最好为该操作添加自定义异常。调试/调查问题将很容易。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-03-21
          • 2011-10-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-11
          • 2020-01-09
          • 1970-01-01
          • 2020-03-01
          相关资源
          最近更新 更多