Java 如何避免创建自定义异常类,同时仍在适当的抽象级别抛出异常?

Java 如何避免创建自定义异常类,同时仍在适当的抽象级别抛出异常?,java,exception,exception-handling,Java,Exception,Exception Handling,我正在回顾我对异常处理的理解(在Java上下文中),并试图找出哪些类型的异常最适合抛出。我经常看到的一条评论是,通常最好避免创建许多自定义异常-最好使用“广泛理解的”标准异常,并且仅“在需要使用附加信息注释异常以帮助对症状进行编程处理时,才创建自定义异常类型。” 然而,这似乎与您应该“在正确的抽象级别抛出异常”的想法有所不同。查看Bob叔叔的“干净代码”中的一个示例,为Employee类提供了以下示例: Bad: public TaxId getTaxId() throws EOFExcepti

我正在回顾我对异常处理的理解(在Java上下文中),并试图找出哪些类型的异常最适合抛出。我经常看到的一条评论是,通常最好避免创建许多自定义异常-最好使用“广泛理解的”标准异常,并且仅“在需要使用附加信息注释异常以帮助对症状进行编程处理时,才创建自定义异常类型。”

然而,这似乎与您应该“在正确的抽象级别抛出异常”的想法有所不同。查看Bob叔叔的“干净代码”中的一个示例,为Employee类提供了以下示例:

Bad: public TaxId getTaxId() throws EOFException
Good: public TaxId getTaxId() throws EmployeeDataNotAvailable
那么,我如何整合这两个“建议”-您应该只在正确的抽象级别抛出异常,并且很少创建自定义异常类。此外,在Java中搜索关于标准异常的信息时,很少有关于哪些标准异常类可用的良好呈现和格式化的信息——我正在寻找在语义上似乎仍然适合调用类的标准异常,但没有找到太多的信息。当然,您可以在jdk文档中找到可用的异常类,但仅仅是缺乏在线信息和讨论似乎很奇怪


这就是我现在的处境。任何建议和意见都将不胜感激

这是一个相当哲学的问题

但一般来说,这意味着您应该在考虑现有异常的情况下创建自己的异常

例如:

在使用某些外部服务的情况下,此服务不可用 ,在这种情况下,我不建议您抛出自己的异常, 因为“连接被拒绝”或“连接超时”可以理解 在现场,每一个程序员在你之后,检查你的自定义 异常程序员将需要去源代码和花费一些时间 在生产中注意到异常后,是时候了解它了 日志


但是,如果我看到我的包装在这种情况下会更清晰,我将添加我的包装。

我认为你的问题没有具体的答案。在我的项目中,对于自定义异常类,我倾向于遵循以下准则:

  • 如果我在方法中遇到异常,请检查异常是否可以由的任何子类或的子类(如果可能)描述。javadocs提供了足够的关于基本类的信息,这些基本类从
    Exception
    RuntimeException
    扩展而来,每个异常类还可以有更多以前没有列出的子类,例如
    IOException
  • 如果没有
    Exception
    RuntimeException
    的子类或任何子类,请创建一个自定义异常类或重用以前创建但带有不同消息的异常类。通常,我倾向于创建这些从
    RuntimeException
    扩展的类,以避免使用
    try-catch
    块的方法的客户端。如果需要在这个方法的客户机中处理异常,那么它应该从
    exception
    扩展
自定义异常类与应用程序中的进程或特定事件相关联

如果开发业务应用程序,那么异常的名称可能与您正在使用的业务流程相关。例如,如果我正在开发一个从一组输入数据(如产品、服务、客户数据等)创建账单的流程,那么我将提供至少2个自定义异常类:

  • ElementNotFoundException
    ,可能是因为找不到特定类型的输入,例如缺少产品或
    Customer#billingAddressLocation
    由于某些客户数据的错误迁移而为
    null
  • BillGenerationException
    ,在收集生成账单所需的数据并在生成账单的确切过程中出现问题时生成

抽象级别由代码的用户判断是对还是错。为了证明AExeption和BEException的存在,应该有一个用户区分它们的用例,例如:

    } catch(AExeption ae) {
        // do something
    } catch(BException be) {
       // do something different
    }  
与以往不同的是:

    } catch(AExeption ae | BException be ) {
        // do something
    }

我的经验是,现实世界中的系统倾向于在症状的编程处理中使用大量的逻辑

使用正确抽象级别的异常和避免创建新的异常类之间没有矛盾。如果可以,您必须为感兴趣的特定方法选择最合适的现有异常类


因此,如果
gettaxid
方法的明确含义不建议该方法执行I/O,那么声明它抛出任何类型的
IOException
都是不合适的。然后,您必须搜索其他现有的异常类,以查找更合适的异常类。如果您没有找到这样一个类,那么您知道创建一个新的异常类是合适的。

我认为Bob叔叔从错误的角度看待这个问题。 您抛出一个异常来分解调用链,并通知非本地逻辑片段发生了意外和有害的事情,并允许它响应

我能理解将一个
EOFEException
和各种不良数据问题包装成一些通用的
InvalidDataException
,但提供一个特定的
EmployeeDataException
似乎有些过分了。 对于调用进程来说,知道(比如)存在本地数据异常而不是(例如)丢失连接可能很有用。这样一来,它可以放弃一个工作单元,但实际上可以继续尝试