在Java中创建异常的一般经验法则是什么?

在Java中创建异常的一般经验法则是什么?

What is the general rule of thumbs for creating an Exception in Java?

我遇到过两种情况:

  • 创建过多的自定义异常
  • 使用太多常规Exception类

在这两种情况下,项目都开始正常,但很快就成了维护(和重构)的开销。

那么关于创建自己的Exception类的最佳实践是什么?


Java专家写了一篇有关Java异常的文章,其中列出了一些创建异常的"最佳实践",总结如下:

  • 不要编写自己的异常(很多有用的异常已经是Java API的一部分)

  • 编写有用的异常(如果您必须编写自己的异常,请确保它们提供有关所发生问题的有用信息)


别做我公司的开发人员。有人创建了一个类似于java.lang.IllegalArgumentException的[sic] InvalidArguementException,现在我们在(字面上)数百个类中使用了它。两者都表明方法已传递了非法或不适当的参数。谈论浪费...

Joshua Bloch在《有效的Java编程语言指南》(最佳实践的第一本圣经)中对此做了介绍。第8章例外项42:赞成使用标准例外。这是他说的话,

Reusing preexisting exceptions has several benefits. Chief among these, it makes your API easier to learn and use because it matches established conventions with which programmers are already familiar [my emphasis, not Bloch's]. A close second is that programs using your API are easier to read because they aren't cluttered with unfamiliar exceptions. Finally, fewer exception classes mean a smaller memory footprint and less time spent loading classes.

The most commonly reused exception is IllegalArgumentException. This is generally the exception to throw when the caller passes in an argument whose value is inappropriate. For example, this would be the exception to throw if the caller passed a negative number in a parameter representing the number of times some action were to be repeated.

也就是说,您永远不要抛出Exception本身。 Java具有一堆精心选择的,多样化且针对性强的内置异常,这些异常可以覆盖大多数情况,并描述发生得很好的异常,以便您可以纠正原因。

对以后必须维护您的代码的程序员友好。


我的经验法则是,当客户端(调用方)可能合理地希望做一些不同的事情时,根据抛出的异常类型,还可以保证其他异常类型。但是,通常不需要多余的异常类型。例如,如果调用方正在编写类似

的代码

1
2
3
4
5
6
7
try {
     doIt();
} catch (ExceptionType1 ex1) {
     // do something useful
} catch (ExceptionType2 ex2) {
     // do the exact same useful thing that was done in the block above
}

那么显然不需要附加的异常类型。我经常看到(或被迫)编写这样的代码,因为被调用的代码在创建新异常类型时过于热情。


如果我找不到名称描述引起错误的类型的异常,那么请自行解决。

那是我的经验法则。


基本上,每个作业都应拥有自己的例外。捕获异常时,您不会像对待对象那样区分不同的实例,因此需要不同的子类型。我看不到使用太多自定义异常的情况。

一种建议是根据需要创建异常,如果很明显一种异常类型是另一种的重复类型,则可以通过合并两者来重构代码。当然,如果从一开始就将某些想法用于构造异常,这将有所帮助。但通常,对于与现有的特定于情况的异常没有1:1对应的所有情况,请使用自定义异常。

另一方面,NullPointerExceptionIndexOutofBoundsException实际上通常是合适的。但是,不要捕获它们(日志记录除外),因为它们是编程错误,这意味着在抛出它们之后,程序处于未定义状态。


创建自己的异常时:

  • 所有异常必须是Throwable类的子代

  • 如果要编写由处理或声明规则自动强制执行的检查异常,则需要扩展Exception Class

  • 如果要编写运行时执行,则需要扩展"运行时异常类"。


我自己的经验法则:

我从来不会抛出异常,除非在单元测试中抛出的异常是无关紧要的,并且没有理由花费任何额外的时间。

我为自定义业务逻辑中发生的错误创建了自己的自定义异常类型。尽可能使用此异常类型来重播其他异常,除非在某些情况下客户端可以看到实际发生的情况。


不要吃异常,将它们抛出https://stackoverflow.com/a/921583/1097600

避免创建自己的异常。使用以下已经存在的名称。

1
2
3
4
5
IllegalStateException
UnsupportedOperationException
IllegalArgumentException
NoSuchElementException
NullPointerException

引发未经检查的异常。

1
2
3
4
public void validate(MyObject myObjectInstance) {
    if (!myObjectList.contains(myObjectInstance))
        throw new NoSuchElementException("object not present in list");
}

推荐阅读