在处理异常时,应该区分 checked 异常和 unchecked 异常。对于 checked 异常,我们应该提供健壮的异常恢复机制,而对于 unchecked 异常,这就是编程错误即 bug ,应该在调试阶段很好的发现和处理它们。
1. Java 异常层次结构

上图(注:该图引自 [url]http://dev2dev.bea.com.cn/techdoc/200702364792.html[/url] )标出了 Java 异常层次结构,也指出了哪些异常是 unchecked ,哪些异常是 checked 。下面给出几段常见的异常处理代理,试图总结日常开发中应该如何处理异常。
2. 针对 checked 异常的恢复机制
checked 异常并不是编程错误,它的出现是软件运行阶段所不可避免的。最常见的这类异常如 socket 连接超时。
对于此类异常,我们应该在程序的运行态下试图从异常中恢复过来。下面这段代码 (Recover.java) 的主要逻辑是,对目标值 protected int current 进行判断,如果该值大于 2 则成功,否则抛出 NotBigEnoughException 异常。
在执行程序的过程中,在每次 catch NotBigEnoughException 异常时,我们对 current 值递增,试图从异常中恢复过来。
NotBigEnoughException.java
package com.zj.exception.types;
 
public class NotBigEnoughException extends Exception {
    public NotBigEnoughException() {
       super ();
    }
 
    public NotBigEnoughException(String msg) {
       super (msg);
    }
}
 
Recover.java
package com.zj.exception;
import com.zj.exception.types.NotBigEnoughException;
 
public class Recover {
    protected int current = 0;
    protected boolean accept = false ;
 
    public Recover() {}
 
    public Recover( int cur) {
       current = cur;
    }
 
    public void increment() {
       ++ current ;
    }
 
    public boolean passed() {
       return accept ;
    }
 
    public void passing() throws NotBigEnoughException {
       if ( current > 2) {
           accept = true ;
           System. out .println( "accept " + current );
       } else
           throw new NotBigEnoughException( "reject " + current );
    }
 
    public static void main(String[] args) {
       Recover re = new Recover();
       while (!re.passed()) {
           try {
              re.passing();
           } catch (NotBigEnoughException e) {
              System. out .println(e);
              re.increment();
           }
       }
    }
}
结果:
com.zj.exception.types.NotBigEnoughException : reject 0
com.zj.exception.types.NotBigEnoughException : reject 1
com.zj.exception.types.NotBigEnoughException : reject 2
accept 3
3. 继承异常
在子类继承父类的情况下,子类 override 方法的异常声明只能取自(小于等于)父类该方法的异常声明;对于子类构造方法的异常声明必须包含(大于等于)父类构造方法的异常声明。
Inheritor 继承自类 Recover 它的方法 passing() 试图声明一个父类没有的异常 UnknowException 这样做是不允许的。
UnknowException.java
package com.zj.exception.types;
 
public class UnknowException extends Exception {
    public UnknowException() {
       super ();
    }
 
    public UnknowException(String msg) {
       super (msg);
    }
}
error in: Inheritor.java
//couldn't throws new exceptions where not found in its base class
public void passing() throws NotBigEnoughException, UnknowException {
    if ( current > 2) {
       accept = true ;
       System. out .println( "accept " + current );
    } else if ( current >= 0)
       throw new NotBigEnoughException( "reject " + current );
    else
       throw new UnknowException( "i don't know how to deal with "
              + current );
}
之所以覆盖这个方法的目的是对父类的 passing() 方法做进一步扩展,对 0<=current<=2 的情况抛出 NotBigEnoughException 而对 current<0 的情况则抛出一个新的异常 UnknowException
此时,提供两种解决方法。
方法一,使用恢复异常机制, overrding passing ()方法,这样可以处理掉所有的异常,因此不需要异常声明。
ok in: Inheritor.java
//sure passing(),so not have to throw exceptions
public void passing(){
    while (!passed()) {
       try {
           super .passing();
       } catch (NotBigEnoughException e) {
           increment();
       }
    }
}
方法二,写一个加强的 passing ()方法,即 fortifiedPassing() 对于在父类 passing ()中捕获的异常,进行再判断。如果是 0<=current<=2 的情况则重新抛出 NotBigEnoughException ,如果是 current<0 的情况则抛出一个新的异常 UnknowException
ok in: Inheritor.java
public void fortifiedPassing() throws NotBigEnoughException, UnknowException{
    try {
       super .passing();
    } catch (NotBigEnoughException e) {
       if ( current >=0)
           throw e;
       else
           throw new UnknowException( "i don't know how to deal with "
                  + current );
    }
}
 
Inheritor.java
package com.zj.exception;
import com.zj.exception.types.NotBigEnoughException;
import com.zj.exception.types.UnknowException;
 
public class Inheritor extends Recover {
    public Inheritor( int cur) {
       super (cur);
    }
 
    //couldn't throws new exceptions where not found in its base class
    /**
    public void passing() throws NotBigEnoughException, UnknowException {
       if (current > 2) {
           accept = true;
           System.out.println("accept " + current);
       } else if (current >= 0)
           throw new NotBigEnoughException("reject " + current);
       else
           throw new UnknowException("i don't know how to deal with "
                  + current);
    }*/
   
    //sure passing(),so not have to throw exceptions
    public void passing(){
       while (!passed()) {
           try {
              super .passing();
           } catch (NotBigEnoughException e) {
              increment();
           }
       }
    }
   
    public void fortifiedPassing() throws NotBigEnoughException, UnknowException{
       try {
           super .passing();
       } catch (NotBigEnoughException e) {
           if ( current >=0)
              throw e;
           else
              throw new UnknowException( "i don't know how to deal with "
                     + current );
       }
    }
 
    public static void main(String[] args) {
       // not required try-catch
       new Inheritor(3).passing();
       new Inheritor(1).passing();
       new Inheritor(-1).passing();   
       //no exceptions
       try {
           new Inheritor(3).fortifiedPassing();
       } catch (NotBigEnoughException e) {
           e.printStackTrace();
       } catch (UnknowException e) {
           System. out .println(e);
       }
       //NotBigEnoughException:
       try {
           new Inheritor(1).fortifiedPassing();
       } catch (NotBigEnoughException e) {
           e.printStackTrace();
       } catch (UnknowException e) {
           System. out .println(e);
       }
       //UnknownException:
       try {
           new Inheritor(-1).fortifiedPassing();
       } catch (NotBigEnoughException e) {
           e.printStackTrace();
       } catch (UnknowException e) {
           System. out .println(e);
       }
    }
}
结果:
accept 3
accept 3
accept 3
accept 3
com.zj.exception.types.UnknowException : i don't know how to deal with -1
com.zj.exception.types.NotBigEnoughException : reject 1
    at com.zj.exception.Recover.passing( Recover.java:28 )
    at com.zj.exception.Inheritor.fortifiedPassing( Inheritor.java:38 )
    at com.zj.exception.Inheritor.main( Inheritor.java:63 )
4.RuntimeException 与包装异常
RuntimeException unhecked 异常,它们由 JVM 抛出(你也可以抛出它),并且不必在异常声明( throws )中列出。
如果 RuntimeException 没有被 catch 而到达 mian ()方法时,那么在程序退出前会自动调用该异常的 printStackTrace() 方法,打印该异常。
RuntimeException 代表的是编程错误(如 0 除数,数组越界),是应该在调试阶段解决的。
当你在捕获某些异常,而不知道该如果处理时,你可以将它包装为 RuntimeException ,这样在后续的方法调用过程中就不用声明 (throws) 该方法了。
在类 Wrapper 中,我们 override fortifiedPassing() 方法,并将它可能抛出的异常包装为 RuntimeException
Wrapper.java
package com.zj.exception;
import com.zj.exception.types.NotBigEnoughException;
import com.zj.exception.types.UnknowException;
 
public class Wrapper extends Inheritor {
    public Wrapper( int cur) {
       super (cur);
    }
 
    public void fortifiedPassing() {
       try {
           super .fortifiedPassing();
       } catch (NotBigEnoughException e) {
           throw new RuntimeException(e);
       } catch (UnknowException e) {
           throw new RuntimeException(e);
       }
    }
 
    public static void main(String[] args) {
       // not required try-catch
       new Wrapper(3).fortifiedPassing();
       new Wrapper(1).fortifiedPassing();
       new Wrapper(-1).fortifiedPassing();
    }
}
结果:
accept 3
Exception in thread "main" java.lang.RuntimeException : com.zj.exception.types.NotBigEnoughException : reject 1
    at com.zj.exception.Wrapper.fortifiedPassing( Wrapper.java:14 )
    at com.zj.exception.Wrapper.main( Wrapper.java:23 )
Caused by: com.zj.exception.types.NotBigEnoughException : reject 1
    at com.zj.exception.Recover.passing( Recover.java:28 )
    at com.zj.exception.Inheritor.fortifiedPassing( Inheritor.java:38 )
    at com.zj.exception.Wrapper.fortifiedPassing( Wrapper.java:12 )
    ... 1 more