欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

异 常 F#

程序员文章站 2022-06-08 21:09:04
...
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
文档应该清楚地指明对象将会处于什么样的状态。




相关标签: F#