提出问题

项目中如何处理异常???

解决问题

以下来自《Effective Java》这本书的笔记:

在这里复习下异常分类:

java中异常分为两类:checked exception(检查异常)和unchecked exception(未检查异常),对于未检查异常也叫RuntimeException(运行时异常).

对未检查的异常(unchecked exception )的几种处理方式:
1、捕获
2、继续抛出
3、不处理

对检查的异常(checked exception,除了RuntimeException,其他的异常都是checked exception )的几种处理方式:

1、继续抛出,消极的方法,一直可以抛到java虚拟机来处理
2、用try…catch捕获

注意,对于受检异常必须处理,或者必须捕获或者必须抛出

例如:

  1. 1)非受检的:NullPointerException,ClassCastException,ArrayIndexsOutOfBoundsException,
  2. ArithmeticException(算术异常,除0溢出)
  3. 2)受检:Exception,FileNotFoundException,IOException,SQLException.

只针对异常的情况才使用异常

异常应该只用于异常情况下,它们永远不应该用于正常控制流中。

错误案例:

try{
   int i = 0;
   while(true){
       range[i++].climb();
   }
}catch(ArrayOutOfBoundsException e){
   //ArrayOutOfBoundsException 是非受检异常,不应该捕获,同时,也不应该
   //在正常代码中处理这种异常    
}

改正:

for(Mountain m:range){
   m.climb();
}

错误案例

try {
   Iterable<Foo> i = collection.iterator();
   while (true){
       Foo foo = i.next();
   }
}catch (NoSuchElementException e){
}

改正:

for(Iterable<Foo> i = collection.iterator();i.hasNext();){
   Foo foo = i.next();
}

对可恢复的情况使用受检异常,对编程错误使用运行时异常

标题已经说的很清楚了,下面简单描述下:

在决定使用受检的异常或是未受检的异常时,主要原则是:如果期望调用者能够适当地恢复,对于这种情况就应该使用受检的异常。

有两种未受检的可抛出结构:运行时异常和错误,它们都是不需要也不应该被捕获的可抛出结构。如果程序抛出未受检的异常或者错误,往往就属于不可恢复的情形,继续执行下去有害无益。

优先使用标准的异常

专家级程序员与缺乏经验的程序员一个最主要的区别在于,专家追求并且通常也能够实现高度的代码

代码重用是值得提倡的,这是一条通用的规则,异常也不例外。

可以这么说,所有错误的方法调用都可以被归结为非法参数或者非法状态,即IllegalArgumentException 和 IllegalArgumentException

抛出与抽象相对应的异常
异常转义:更高层的实现应该捕获底层的异常,同时抛出可以按照高层抽象进行解释的异常。

例如:

try{
       //use lower-level abstraction to do our bidding
       ...
   }catch(LowerLevelException e){
       throw new HigherLevelException(...);
   }    
   public void uploadImageFile(String imagePath)  {
       try {
           /上传图片文件
       }catch(IOException e) {
          //把原始异常信息记录到日记中(便于排错)
           throw  new 异常构造方法();
    }
 }

异常链:将异常发生的原因一个传一个串起来,即把底层的异常信息传给上层,这样逐层抛出.

try {
    lowLevelOp();
}catch (LowLevelException le) {
    throw (HighLevelException)
     new HighLevelException().initCause(le);
}

总而言之,如果不能阻止或者处理来自更底层的异常,一般的做法是使用异常转译,除非底层方法碰巧可以保证它跑车的所有异常也适合才可以将异常从底层传播到高层。异常链对高层和底层异常都提供了最佳的功能;它允许抛出适当的高层异常,同时又能捕获底层的原因进行失败分析。

每个方法抛出的异常都要有文档
始终要单独地声明受检的异常,并且利用Javadoc的@throws标记,准确地记录下抛出每个异常的条件。

具体可以参考这篇文章:检查异常和未检查异常不同之处

总而言之,要为你编写的每个方法所能抛出的每个异常建立文档。对于未受检和受检的异常,以及对于抽象的和具体的方法都一样。要为每个受检异常提供单独的throws子句,不要为未受检的异常提供throws子句。如果没有为可以抛出的异常建立文档,其他人就很难或者根本不可能有效地使用你的类或者接口。

在细节消息中包含能捕获失败的信息

为了捕获异常,异常的细节信息应该包含所有“对该异常有贡献”的参数和域的值。

例如:

/**
* Constructs an <code>IndexOutOfBoundsException</code> with the
* specified detail message.
*
* @param   s   the detail message.
*/
//这个是IndexOutOfBoundsException这个异常的构造器
public IndexOutOfBoundsException(String s) {
   super(s);
}

期望:

/**
* @param lowerBound
* @param upperBound
* @param index
*/
//通过改造后的异常可以包含足够的能捕获失败的信息
public IndexOutOfBoundsException(int lowerBound,int upperBound,int index){
   super("Lower bound: " + lowerBound +
   ",Upper bound: " + upperBound +
   ", Index:" + index);
   this.lowerBound = lowerBound;
   this.upperBound = upperBound;
   this.index = index;
}

努力使失败保持原子性

失败原子性:失败的方法调用应该使对象保持在被调用之前的状态。

对于可变对象上执行操作的方法,获得失败原子性最常见的办法是,在执行操作之前检查参数的有效性。这可使得在对象的状态被修改之前,先抛出适当的异常。例如:

public Object pop(){
   //如果取消对size检查,会导致size域保持不一致的状态
   if(size == 0){
       throw new EmptyStackException();
   }
   Object result = elements[--size];
   elements[size] = null;//
   return result;
}

另外一种获得失败原子性的办法是,调整计算处理过程的顺序,使得任何可能会失败的计算部分都在对象状态被修改之前发生。

最后一种获得失败原子性的办法是,在对象的一份临时拷贝上执行操作,当操作完成之后,再用临时拷贝中的结果代替对象的内容。

不要忽略异常

当API的设计者声明一个方法将抛出某个异常的时候,他们等于正在视图说明某些事情。所有请不要忽略它!

例如:

try{
   ...
}catch(SomeException e){
}

上面的例子中,是一个空的catch块,空的catch块会使异常达不到应有的目的,