异 常 F#
程序员文章站
2022-06-08 21:08:16
...
a>:异常只应该被用于不正常的条件,它们永远不应该被用于正常的控制流.
b>:一个设计良好的API不应该强迫它的客户为了正常的控制流而使用异常。如Iterator<T>, next()状态方法,hasNext()状态测试方法.
pubilc interface Iterator<T> {
public boolean hasNext();
public T next();
public void remove();
}
"状态测试方法"和"可被识别的返回值"这两种做法选择的指导原则.
c>:用运行时异常来指明程序错误.也就是说,检查应该由调用方来完成,而调用方未保证条件的正确性,刚导致异常的出现,此时应该
使用RuntimeException.
对于可恢复的使用Exception,它也相当于一个方法的除了正常返回值的第二个返回值.
异常要表达:出了什么错误,在那儿出错了,出错的信息收集,建议的解决办法.
public IndexOutOfBoundsException(int lowerBound,int upperBound,int index) {
//记录发生异常时的状态值.
super("Lower bound: " + lowerBound + ", Upper bound: " + upperBound + ", Index: " + index);
}
d>:异常转译,出现频率最高的:将Exception转换为RuntimeException抛出.
高层的实现应该捕获低层的异常,同时抛出一个可以按照高层抽象进行解释的异常,这种做法被称为异常转译(exception translation).
尽管异常转译比不加选择地传递低层异常的做法有所改进,但是它也不能被滥用。如果可能的话,处理来自低层的最好做法是,在
调用低层方法之前确保它们会成功执行,从而避免它们会抛出异常。有时候,你可以在给低层传递实参之前,显式地检查这些实参
的有效性,从而避免低层方法会抛出异常, 其次是让高层来处理这些异常,从而将高层方法的调用者与低层的问题隔离开.
如果既不能阻止来自低层的异常,也无法将它们与高层隔离开,那么一般的做法是使用异常转译。只有在低层方法的规范碰巧可以
保证“它所抛出的异常对于高层也是合适的”情况下,才可以将异常从低层传播到高层.
e>:业务处理中可能要定义一套自己的业务异常.
但尽量使用标准的异常.
IllegalArgumentException 参数的值不合适
IllegalStateException 对于这个方法调用而言,对象状态不合适.
NullPointerException 在null被禁止的情况下参数值为null
IndexOutOfBoundException 下标超界
ConcurrentModificationException 在禁止并发修改的情况下,对象检测到并发修改
UnsupportedOperationException 对象不支持客户请求的方法
ArithmeticException
NumberFormatException
f>:每个方法抛出的异常都要有文档.
一但方法可能抛出异常,不管是Exception还是RuntimeException,都需要在doc中进行描述,特别是RuntimeException
对于一个方法可能抛出的未被检查的异常,如果将这些异常信息很好地组织成一个列表文档,则可以有效地描述出这个方法
被成功执行的前提条件。
g>:失败的原子性.
不可变对象自动保证失败的原子性.
对于在可变对象上执行操作的方法,获得失败原子性最常见的办法是,在执行操作之前检查参数的有效性。这可以使得在对象
的状态被修改之前,适当的异常首先被抛出来.
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;//Eliminate obsolete reference
return result;
}
如果初始的大小(size)检查被处掉的话,当这个方法企图从一个empty stack 中弹出元素时,它仍然会抛出一个异常。然而,这将会
导致size域保持在不一致的状态(负数)中,从而使得将来对该对象的任何方法调用都会失败.而且,那时候,pop方法抛出的异常对于
该stack抽象来说也是不恰当的.
另一种类似的获得失败原子性的办法是,对计算处理过程调整顺序,使得任何可能会失败的计算部分都发生在对象状态被修改之前.
在向TreeMap中添加一个元素,该元素的类型必须可能过TreeMap的排序准则与其它的元素进行比较,企图增加一个类型不正确的
元素将导致ClassCastException异常.这实质上是上一种办法的变体.都是在真正修改对象状态之前,抛出异常.
第三种办法也一段恢复代码.解释操作过程中发生的失败,以及使对象回滚到操作开始之前的状态上.
最后一种办法就是在对象一份临时拷贝上执行操作,当操作完成之后再把临时拷贝中的结果复制给原来的对象.这样做的原因
可能是出于性能的考虑,也有一个附加好处,即使失败了,对象的状态仍保持原样.
总结一条规则:作为方法规范的一部分,任何一个异常都不应该改变对象调用该方法之前的状态.如果这条规则被违反,则API
文档应该清楚地指明对象将会处于什么样的状态。
b>:一个设计良好的API不应该强迫它的客户为了正常的控制流而使用异常。如Iterator<T>, next()状态方法,hasNext()状态测试方法.
pubilc interface Iterator<T> {
public boolean hasNext();
public T next();
public void remove();
}
"状态测试方法"和"可被识别的返回值"这两种做法选择的指导原则.
c>:用运行时异常来指明程序错误.也就是说,检查应该由调用方来完成,而调用方未保证条件的正确性,刚导致异常的出现,此时应该
使用RuntimeException.
对于可恢复的使用Exception,它也相当于一个方法的除了正常返回值的第二个返回值.
异常要表达:出了什么错误,在那儿出错了,出错的信息收集,建议的解决办法.
public IndexOutOfBoundsException(int lowerBound,int upperBound,int index) {
//记录发生异常时的状态值.
super("Lower bound: " + lowerBound + ", Upper bound: " + upperBound + ", Index: " + index);
}
d>:异常转译,出现频率最高的:将Exception转换为RuntimeException抛出.
高层的实现应该捕获低层的异常,同时抛出一个可以按照高层抽象进行解释的异常,这种做法被称为异常转译(exception translation).
尽管异常转译比不加选择地传递低层异常的做法有所改进,但是它也不能被滥用。如果可能的话,处理来自低层的最好做法是,在
调用低层方法之前确保它们会成功执行,从而避免它们会抛出异常。有时候,你可以在给低层传递实参之前,显式地检查这些实参
的有效性,从而避免低层方法会抛出异常, 其次是让高层来处理这些异常,从而将高层方法的调用者与低层的问题隔离开.
如果既不能阻止来自低层的异常,也无法将它们与高层隔离开,那么一般的做法是使用异常转译。只有在低层方法的规范碰巧可以
保证“它所抛出的异常对于高层也是合适的”情况下,才可以将异常从低层传播到高层.
e>:业务处理中可能要定义一套自己的业务异常.
但尽量使用标准的异常.
IllegalArgumentException 参数的值不合适
IllegalStateException 对于这个方法调用而言,对象状态不合适.
NullPointerException 在null被禁止的情况下参数值为null
IndexOutOfBoundException 下标超界
ConcurrentModificationException 在禁止并发修改的情况下,对象检测到并发修改
UnsupportedOperationException 对象不支持客户请求的方法
ArithmeticException
NumberFormatException
f>:每个方法抛出的异常都要有文档.
一但方法可能抛出异常,不管是Exception还是RuntimeException,都需要在doc中进行描述,特别是RuntimeException
对于一个方法可能抛出的未被检查的异常,如果将这些异常信息很好地组织成一个列表文档,则可以有效地描述出这个方法
被成功执行的前提条件。
g>:失败的原子性.
不可变对象自动保证失败的原子性.
对于在可变对象上执行操作的方法,获得失败原子性最常见的办法是,在执行操作之前检查参数的有效性。这可以使得在对象
的状态被修改之前,适当的异常首先被抛出来.
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;//Eliminate obsolete reference
return result;
}
如果初始的大小(size)检查被处掉的话,当这个方法企图从一个empty stack 中弹出元素时,它仍然会抛出一个异常。然而,这将会
导致size域保持在不一致的状态(负数)中,从而使得将来对该对象的任何方法调用都会失败.而且,那时候,pop方法抛出的异常对于
该stack抽象来说也是不恰当的.
另一种类似的获得失败原子性的办法是,对计算处理过程调整顺序,使得任何可能会失败的计算部分都发生在对象状态被修改之前.
在向TreeMap中添加一个元素,该元素的类型必须可能过TreeMap的排序准则与其它的元素进行比较,企图增加一个类型不正确的
元素将导致ClassCastException异常.这实质上是上一种办法的变体.都是在真正修改对象状态之前,抛出异常.
第三种办法也一段恢复代码.解释操作过程中发生的失败,以及使对象回滚到操作开始之前的状态上.
最后一种办法就是在对象一份临时拷贝上执行操作,当操作完成之后再把临时拷贝中的结果复制给原来的对象.这样做的原因
可能是出于性能的考虑,也有一个附加好处,即使失败了,对象的状态仍保持原样.
总结一条规则:作为方法规范的一部分,任何一个异常都不应该改变对象调用该方法之前的状态.如果这条规则被违反,则API
文档应该清楚地指明对象将会处于什么样的状态。