专家级程序员与缺乏经验的程序员的一个主要区别是专家总是努力追求并且能获得代码的高度复用。好的代码应能复用,这是一条普遍规则,异常也不例外。Java平台库提供了常见未受检查异常的集合,它覆盖了大多数API需要抛出的大部份异常。本条目讨论这些广为复用的异常。
重用已存在的异常有几方面的好处。其中主要的是,这使你的API易于学习与使用,因为它符合程序员已熟悉的广为接收的习惯用法。与此紧密相关的第二点是,使用这种API的程序变得易读,因为这种程序没有不熟悉的异常,不会引起混乱。最后,较少的异常类意味着较少的内存及较少装载类的时间。
最普遍使用的异常是IllegalArgumentException。如果调用者传入的参数值不正确,一般就抛出这个异常。例如,如果调用者传入的一个参数为负数,而这个参数表示某个重复动作的次数,就应抛出这个异常。
另一个常用异常是IllegalStateException.。如果方法调用因接收对象的状态而非法,一般就抛出这个异常。例如,如果调用者试图使用未正常初始化的对象,就应抛出这个异常。
当然,所有的错误方法调用总会归于非法的参数或非法的参数状态,但其它的异常总是用于特定的非法参数与非法状态。如果参数不允许为null,而调用者传入一个null值,则按惯例应抛出NullPointerException异常,而不是IllegalStateException异常,类似的,若参数值表示一个序列的索引,而调用者传入的值不在索引值范围内,则应抛出IndexOutOfBoundsException异常,而不是IllegalStateException。
另一个值得熟悉的且有广泛用途的异常是ConcurrentModificationException。若一个对象被设计用于单线程或外部同步,而又发现这个对象被并发的修改,哪么就应抛出这个异常。
最后一个值得一提且有广泛用途的异常是UnsupportedOperationException。如果一个对象不支持所希望的操作就抛出这个异常。与本条目中讨论的其它异常相比,这个异常很少使用,因为大部分对象支持所实现的所有方法。如果没有实现接口中定义的可选操作,就使用这个异常。例如,对于只允许增加的List的实现,如果有人试图删除list中的元素,就应抛出这个异常。
这个表总结了大部分被重用的异常。
异常 | 使用时机 |
IllegalArgumentException | 非null值,不合适的参数值 |
IllegalStateException | 对象状态不适合方法调用 |
NullPointerException | 参数值是null,但这不允许 |
IndexOutOfBoundsException | 索引参数值越界 |
ConcurrentModificationException | 对象被并发修改,但这是禁止的 |
UnsupportedOperationException | 对象不支持的方法 |
当然,这些仅是Java平台上最常见的可重用异常,只要条件合适,其它异常也可被常用。例如,如果你在实现复数与有理数这样的算术对象时,重用ArithmeticException和Number
FormatException这样的异常是合适的。如果异常适合你的需要,就用它,当然这要在你抛出的异常与异常文档是一致的情况下。重用必须基于语义,而不仅是名字。若增加更多的异常信息,则可子例化已存在的异常。
最后,请注意选择重用哪个异常并总是一定科学的,就如有时上表中异常的使用不是互斥的。考虑这样一个例子,一个对象表示一副纸牌,设想有一个方法处理纸版的一手牌,这个方法有一个参数表示这手牌的张数,如果一个调用者给这个参数传入的值大于剩下牌的张数,这可用IllegalArgumentException异常(张数参数值太大)也可用IllegalStateException异常(对这次请求,纸牌对象所含牌张数太少)。就这个情形,感觉IllegalArgumentException是正确的,但也没有必须遵守的规则。