我遇到过两种情况:
- 创建太多自定义异常
- 使用过多的通用异常类
在这两种情况下,项目都开始正常,但很快就成为维护(和重构)的开销。
那么关于创建自己的异常类的最佳实践是什么?
不要像我公司的开发人员那样做。有人创建了一个与 java.lang.IllegalArgumentException 类似的 [sic] InvalidArguementException,我们现在(实际上)在数百个类中使用它。两者都表明一个方法传递了一个非法或不适当的参数。说废话...
Joshua Bloch 在Effective Java Programming Language Guide [我关于最佳实践的首选圣经]第 8 章. Exceptions Item 42: Favor the use of standard exceptions中对此进行了介绍。这是他说的一点点,
重用预先存在的异常有几个好处。其中最主要的是,它使您的 API 更易于学习和使用,因为它符合程序员已经熟悉的既定约定(我强调,而不是 Bloch 的)。紧随其后的是使用您的 API 的程序更易于阅读,因为它们不会被不熟悉的异常所困扰。最后,更少的异常类意味着更小的内存占用和更少的加载类的时间。
最常重用的异常是 IllegalArgumentException。这通常是调用者传入值不合适的参数时抛出的异常。例如,如果调用者在参数中传递了一个负数,表示某些操作将被重复的次数,这将是抛出的异常。
也就是说,你永远不应该抛出 Exception 本身。Java 有一组精心挑选、多样化且目标明确的内置异常,它们涵盖了大多数情况,并充分描述了发生的异常,以便您可以纠正原因。
对将来必须维护您的代码的程序员保持友好。
我的经验法则是,当客户端(调用者)可能合理地想要做一些不同的事情时,根据抛出的异常类型,额外的异常类型是有保证的。然而,通常不需要额外的异常类型。例如,如果调用者正在编写类似的代码
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 对应关系的案例,请使用自定义例外。
另一方面,NullPointerException
s 和IndexOutofBoundsException
s 实际上可能经常是合适的。但是不要抓住这些(除了日志记录),因为它们是一个编程错误,这意味着在抛出它们之后,程序处于未定义状态。
我自己的经验法则:
我从不抛出异常,除非在单元测试中你抛出的内容无关紧要并且没有理由在它上面花费任何额外的时间。
我为自定义业务逻辑中发生的错误创建了自己的自定义异常类型。这种异常类型尽可能多地用于重铸其他异常,除非在客户端可以了解实际发生的情况的情况下。
在创建自己的异常时:
所有异常都必须是Throwable 类的子类
如果要编写由 Handle 或 Declare Rule 自动强制执行的已检查异常,则需要扩展Exception Class
如果要编写运行时执行,则需要扩展运行时异常类。
避免创建自己的异常。使用下面已经存在的。
IllegalStateException
UnsupportedOperationException
IllegalArgumentException
NoSuchElementException
NullPointerException
抛出未经检查的异常。
public void validate(MyObject myObjectInstance) {
if (!myObjectList.contains(myObjectInstance))
throw new NoSuchElementException("object not present in list");
}