以下内容不完全是自己的原创,有参考了http://www.cnblogs.com/jing8100/archive/2010/04/02/1703037.html 这个网友的
JAVA的异常,对应什么是异常,我就不多说,主要是讲异常使用的时候需要注意些什么,异常是很简单一件事情,别被网上很多讲迷惑了
java的异常层次:
从上图可以看到 Throwable是父类,它直接从Object基类继承了,下面派生出两种类别,一种是Error,一种是Exception
Error :一般的程序是不需要关心的,但是如果你不按照常规,在自己的程序抛出Error,然后try,catch进行处理的话 也是成功的,因为Error是继承Throwable,try catch( Throwable e) ,所以对Error来说也是可以捕获到的,都是运行时异常(后面有讲)
Exception: 分为两种,一种是RunTimeException ,也叫unchecked异常比如数字越界,空指针,等都是运行时异常,调用可以try cath 或者什么都不做(默认是往外层)。
一种是checked异常,这类异常抛出去以后,调用着必须try ,catch 或者往外抛。数据库连接异常以及典型的IO异常,ClassNotFound都属于这类异常
最直接的表现形式在代码上是如下:
-
public class ME {
public static void main(String[] args){
//当程序执行过程中,遇到uncheck exception,则程序中止,不再执行之后的代码。
test1();
test2();
try {
test3();
} catch (Exception e) {
throw new RuntimeException(e);
}
try {
test4();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//运行时异常:方法内部抛出的异常,则throw即可,调用者可捕获也可不捕获。public static void test1(){
System.out.println("invoke test1");
throw new IllegalArgumentException();
}
//uncheck exception,调用时候不需要捕获 ,声明throws即可,调用者可捕获也可不捕获
public static void test2() throws IllegalArgumentException{
System.out.println("invoke test2");
}
//check exception,调用的时候必须捕获
public static void test3() throws Exception{
System.out.println("invoke test3");
}
//check exception,调用的时候必须捕获
public static void test4() throws Exception {
System.out.println("invoke test3");
throw new Exception();
}
}
其实像RunTimeException 如果调用者不捕获,则会一直外抛,知道抛到main函数或者Thread.run(多线程)
何时定义RunTimeException 何时定义 Checked异常呢 ,有下面三个参考
(1) 该异常发生后是可以被恢复的,如一个Internet连接发生异常被中止后,可以重新连接再进行后续操作。
(2) 程序依赖于不可靠的外部条件,该依赖条件可能出错,如系统IO。
(3) 该异常发生后并不会导致程序处理错误,进行一些处理后可以继续后续操作。
如果调用者捕获到异常后有恢复程序的能力,则可以抛出Checked异常,让调用者明确知道你会抛出什么异常,他能采取什么措施
下面说说
try {
}
catch(e1){
}
catch( e2){
}
finally {
}
这样的顺序:
e1,e2的顺序,要先子类,后父类
因为当异常产生时,JVM会按照顺序查找与异常匹配的catch块,如果把catch(Exception e)放在所有catch块的最前面,由于大多数RuntimeException类都继承了Exception类,所有的异常都会被catch(Exception e)所捕捉。这意味着其它的catch块都不会被执行。所以catch块的排列顺序应该是按照先子类、后父类的方式排列,最后才是catch(Exception e)
使用finally块释放资源
finally 最好不要抛出异常,因为在捕获到异常后,jvm会先执行fianlly里面的代码,执行完毕才回到catch里面的代码,这样如果
finally关键字保证无论程序使用任何方式离开try块,finally中的语句都会被执行。在以下三种情况下会进入finally块:
(1) try块中的代码正常执行完毕。
(2) 在try块中抛出异常。
(3) 在try块中执行return、break、continue。
因此,当你需要一个地方来执行在任何情况下都必须执行的代码时,就可以将这些
代码放入finally块中。当你的程序中使用了外界资源,如数据库连接,文件等,必须将释放这些资源的代码写入finally块中。
必须注意的是,在finally块中不能抛出异常。JAVA异常处理机制保证无论在任何情况下必须先执行finally块然后在离开try块,因此在try块中发生异常的时候,JAVA虚拟机在执行catch()的代码后,先转到finally块执行finally块中的代码,finally块执行完毕后,再向外抛出异常。如果在finally块中抛出异常,try块捕捉的异常就不能抛出,因为被finally抛出异常覆盖了,外部捕捉到的异常就是finally块中的异常信息,而try块中发生的真正的异常堆栈信息则丢失了。(在catch里面同样可以抛出异常,但是也会覆盖掉try里面的异常,执行顺序为
try catch finally, 后面的异常会覆盖前面的异常。)
异常不能影响对象的状态(失败原子性,在practical java里面有讲到)
异常产生后不能影响对象的状态,这是异常处理中的一条重要规则。 在一个函数
中发生异常后,对象的状态应该和调用这个函数之前保持一致,以确保对象处于正确的状态中。
如果对象是不可变对象(不可变对象指调用构造函数创建后就不能改变的对象,即
创建后没有任何方法可以改变对象的状态),那么异常发生后对象状态肯定不会改变。如果是可变对象,必须在编程中注意保证异常不会影响对象状态。有三个方法可以达到这个目的:
(1) 将可能产生异常的代码和改变对象状态的代码分开,先执行可能产生异常的代码,如果产生异常,就不执行改变对象状态的代码。
(2) 对不容易分离产生异常代码和改变对象状态代码的方法,定义一个recover方法,在异常产生后调用recover方法修复被改变的类变量,恢复方法调用前的类状态。
(3) 在方法中使用对象的拷贝,这样当异常发生后,被影响的只是拷贝,对象本身不会受到影响。
异常转换的时候,丢失异常信息
,在JDK1.4.1中,Throwable类增加了两个构造方法,public Throwable(Throwable
cause)和public Throwable(String message,Throwable cause),在构造函数中传入的原始异常堆栈信息将会在printStackTrace方法中打印出来