java异常处理

来源:互联网 发布:自学java三个月找工作 编辑:程序博客网 时间:2024/06/07 00:30

 Java异常处理原理

一、基础知识

1 Throwable类及其子类

类Throwable有两个直接子类:Error和Exception。Error类对象(如动态连接错误等),由Java虚拟机生成并抛弃(通常,Java程序不对这类 异常进行处理) ;Exception类对象是Java程序处理或抛弃的对象。它有各种不同的子类分别对应于不同类型的例外。其中类RuntimeException代表运行时由Java虚拟机生成的 异常,如算术运算异常ArithmeticException(由除 0错等导致)、数组越界例外ArrayIndexOutOfBoundsException等;其它则为非运行时异常,如输入输出例外 IOException等。Java编译器要求Java程序必须捕获或声明所有的非运行时异常,但对运行时异常可以不做处理。

2 异常处理关键字

Java的异常处理是通过5个关键字来实现的:try,catch,throw,throws,finally。JB的在线帮助中对这几个关键字是这样解释的:

Throws:  Lists the exceptions a method could throw.

Throw:   Transfers control of the method to the exception handler.

Try:    Opening exception-handling statement.

Catch:  Captures the exception.

Finally: Runs its code before terminating the program.

2.1try语句 
try语句用大括号{}指定了一段代码,该段代码可能会抛弃一个或多个例外。

2.2catch语句 
catch语句的参数类似于方法的声明,包括一个例外类型和一个例外对象。例外类型必须为Throwable类的子类,它指明了catch语句所处理的例外类型,例外对象则由运行时系统在try所指定的代码块中生成并被捕获,大括号中包含对象的处理,其中可以调用对象的方法。

catch语句可以有多个,分别处理不同类的例外。Java运行时系统从上到下分别对每个catch语句处理的例外类型进行检测,直到找到类型相匹配的catch语句为止。这里,类型匹配指catch所处理的例外类型与生成的例外对象的类型完全一致或者是它的父类,因此,catch语句的排列顺序应该是从特殊到一般。  

也可以用一个catch语句处理多个例外类型,这时它的例外类型参数应该是这多个例外类型的父类,程序设计中要根据具体的情况来选择catch语句的例外处理类型。 

2.3  finally语句 
try所限定的代码中,当抛弃一个例外时,其后的代码不会被执行。通过finally语句可以指定一块代码。无论try所指定的程序块中抛弃或不抛弃例外,也无论catch语句的例外类型是否与所抛弃的例外的类型一致,finally所指定的代码都要被执行,它提供了统一的出口。通常在finally语句中可以进行资源的清除工作。如关闭打开的文件等。

2.4 throws语句 
throws总是出现在一个函数头中,用来标明该成员函数可能抛出的各种异常。对大多数Exception子类来说,Java 编译器会强迫你声明在一个成员函数中抛出的异常的类型。如果异常的类型是Error或 RuntimeException, 或它们的子类,这个规则不起作用,因为这在程序的正常部分中是不期待出现的。如果你想明确地抛出一个RuntimeException,你必须用throws语句来声明它的类型。

2.5throw语句 
throw总是出现在函数体中,用来抛出一个异常。程序会在throw语句后立即终止,它后面的语句执行不到,然后在包含它的所有try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块。
3  关键字及其中语句流程详

3.1try的嵌套
你可以在一个成员函数调用的外面写一个try语句,在这个成员函数内部,写另一个try语句保护其他代码。每当遇到一个try语句,异常的框架就放到堆栈上面,直到所有的try语句都完成。如果下一级的try语句没有对某种异常进行处理,堆栈就会展开,直到遇到有处理这种异常的try语句。
相对于try-catch-finally程序块而言,try-catch的执行流程以及执行结果还是比较简单的。

3.2 try-catch程序块的执行流程以及执行结果

首先执行的是try语句块中的语句,这时可能会有以下三种情况:

1. 如果try块中所有语句正常执行完毕,那么就不会有其他的“动做”被执行,整个try-catch程序块正常完成。

2.如果try语句块在执行过程中碰到异常V,这时又分为两种情况进行处理:

    如果异常V能够被与try相应的catch块catch到,那么第一个catch到这个异常的catch块(也是离try最近的一个与异常V匹配的catch块)将被执行;如果catch块执行正常,那么try-catch程序块的结果就是“正常完成”;如果该catch块由于原因R突然中止,那么try-catch程序块的结果就是“由于原因R突然中止(completes abruptly)”。

     如果异常V没有catch块与之匹配,那么这个try-catch程序块的结果就是“由于抛出异常V而突然中止(completes abruptly)”。

3.如果try由于其他原因R突然中止(completes abruptly),那么这个try-catch程序块的结果就是“由于原因R突然中止(completes abruptly)”。

3.3 try-catch-finally程序块的执行流程以及执行结果
try-catch-finally程序块的执行流程以及执行结果比较复杂。

首先执行的是try语句块中的语句,这时可能会有以下三种情况:

1.如果try块中所有语句正常执行完毕,那么finally块的居于就会被执行,这时分为以下两种情况:

如果finally块执行顺利,那么整个try-catch-finally程序块正常完成。

如果finally块由于原因R突然中止,那么try-catch-finally程序块的结局是“由于原因R突然中止(completes abruptly)”

2.如果try语句块在执行过程中碰到异常V,这时又分为两种情况进行处理:

2.1如果异常V能够被与try相应的catch块catch到,那么第一个catch到这个异常的catch块(也是离try最近的一个与异常V匹配的catch块)将被执行;这时就会有两种执行结果:

2.1.1如果catch块执行正常,那么finally块将会被执行,这时分为两种情况:

2.1.1.1如果finally块执行顺利,那么整个try-catch-finally程序块正常完成。

2.1.1.2如果finally块由于原因R突然中止,那么try-catch-finally程序块的结局是“由于原因R突然中止(completes abruptly)”

2.1.2如果catch块由于原因R突然中止,那么finally模块将被执行,分为两种情况:

2.1.2.1如果如果finally块执行顺利,那么整个try-catch-finally程序块的结局是“由于原因R突然中止(completes abruptly)”。

2.1.2.2如果finally块由于原因S突然中止,那么整个try-catch-finally程序块的结局是“由于原因S突然中止(completes abruptly)”,原因R将被抛弃。

注:如果在你的代码中期望通过捕捉被调用的下级函数的异常来给定返回值,那么一定要注意你所调用的下级函数中的finally语句,它有可能会使你throw出来的异常并不能真正被上级调用函数可见的。当然这种情况是可以避免的,去掉被调用函数的finally中的return就可以了。  

2.2如果异常V没有catch块与之匹配,那么finally模块将被执行,分为两种情况:

2.2.1如果finally块执行顺利,那么整个try-catch-finally程序块的结局就是“由于抛出异常V而突然中止(completes abruptly)”。

2.2.2 如果finally块由于原因S突然中止,那么整个try-catch-finally程序块的结局是“由于原因S突然中止(completes abruptly)”,异常V将被抛弃。

3如果try由于其他原因R突然中止(completes abruptly),那么finally块被执行,分为两种情况:

3.1如果finally块执行顺利,那么整个try-catch-finally程序块的结局是“由于原因R突然中止(completes abruptly)”。

3.2如果finally块由于原因S突然中止,那么整个try-catch-finally程序块的结局是“由于原因S突然中止(completes abruptly)”,原因R将被抛弃。

3.4   try-catch-finally程序块中的return
从上面的try-catch-finally程序块的执行流程以及执行结果一节中可以看出无论try或catch中发生了什么情况,finally都是会被执行的,那么写在try或者catch中的return语句也就不会真正的从该函数中跳出了,它的作用在这种情况下就变成了将控制权(语句流程)转到finally块中;这种情况下一定要注意返回值的处理。

例如,在try或者catch中return false了,而在finally中又return true,那么这种情况下不要期待你的try或者catch中的return false的返回值false被上级调用函数获取到,上级调用函数能够获取到的只是finally中的返回值,因为try或者catch中的return语句只是转移控制权的作用。

注:java中return和finally问题!
1.finally语句在return语句执行完了以后才执行的
2.finally语句块里面的return把原来的return给覆盖了!!变成了新的返回值了
3.如果finally语句中没有返回语句覆盖的话,那么原来的返回值就不会变,不管你是不是改变了要返回的那个变量

4    异常的分类

1
异常的继承结构:基类为Throwable,Error和Exception继承Throwable,RuntimeException和IOException等继承Exception,具体的RuntimeException继承RuntimeException。
2
Error和RuntimeException及其子类成为未检查异常(unchecked),其它异常成为已检查异常(checked)。
每个类型的异常的特点
2.1 Error体系
Error类体系描述了Java运行系统中的内部错误以及资源耗尽的情形。应用程序不应该抛出这种类型的对象(一般是由虚拟机抛出)。如果出现这种错误,除了尽力使程序安全退出外,在其他方面是无能为力的。所以,在进行程序设计时,应该更关注Exception体系。
2.2 Exception体系
Exception体系包括RuntimeException体系和其他非RuntimeException的体系
2.2.1 RuntimeException
RuntimeException体系包括错误的类型转换、数组越界访问和试图访问空指针等等。处理RuntimeException的原则是:如果出现RuntimeException,那么一定是程序员的错误。例如,可以通过检查数组下标和数组边界来避免数组越界访问异常。
2.2.2 其他(IOException等等)
这类异常一般是外部错误,例如试图从文件尾后读取数据等,这并不是程序本身的错误,而是在应用环境中出现的外部错误。

二、异常处理的一般规则   

1、 能处理就早处理,抛出不去还不能处理的就想法消化掉或者转换为RuntimeException处理。因为对于一个应用系统来说,抛出大量异常是有问题的,应该从程序开发角度尽可能的控制异常发生的可能。

2、 对于检查异常,如果不能行之有效的处理,还不如转换为RuntimeException抛出。这样也让上层的代码有选择的余地――可处理也可不处理。
3、 对于一个应用系统来说,应该有自己的一套异常处理框架,这样当异常发生时,也能得到统一的处理风格,将优雅的异常信息反馈给用户。

三、异常链

准备:
Throwable类中的常用方法
 getCause():返回抛出异常的原因。如果 cause 不存在或未知,则返回 null。
 getMessage():返回异常的消息信息。
 printStackTrace():对象的堆栈跟踪输出至错误输出流,作为字段 System.err 的

异常链顾名思义就是将异常发生的原因一个传一个串起来,即把底层的异常信息传给上层,这样逐层抛出。Java API文档中给出了一个简单的模型:
try { lowLevelOp(); } catch (LowLevelException le) { throw new HighLevelException(le); // Chaining-aware constructor }

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

当程序捕获到了一个底层异常le,在处理部分选择了继续抛出一个更高级别的新异常给此方法的调用者。这样异常的原因就会逐层传递。这样,位于高层的异常递 归调用getCause()方法,就可以遍历各层的异常原因。这就是Java异常链的原理。

四、总结

总结一下Java异常处理的要点: 

  1、 异常是程序运行过程过程出现的错误,在Java中用类来描述,用对象来表示具体的异常。Java将其区分为Error与Exception,Error是程序无力处理的错误,Exception是程序可以处理的错误。异常处理是为了程序的健壮性。
 2、 Java异常类来自于Java API定义和用户扩展。通过继承Java API异常类可以实现异常的转译。
 3、 异常能处理就处理,不能处理就抛出,最终没有处理的异常JVM会进行处理
 4、 异常可以传播,也可以相互转译,但应该根据需要选择合理的异常转译的方向。
 5、 对于一个应用系统,设计一套良好的异常处理体系很重要。这一点在系统设计的时候就应该考虑到。
 6、"异常机制"中还有一种特殊情况――RuntimeException"异常类"见图),这个"异常类"和它的所有子类都有一个特性,就是"异常"对象一产生就被Java虚拟机直接处理掉,即在方法中出现throw 子句的地方便被虚拟机捕捉了。因此凡是抛出这种"运行时异常"的方法在被引用时,不需要有try…catch语句来处理"异常"。
7、方法与"异常"的关联可以一直向上传递,当传递到与main方法关联后,即在main()方法的定义中使用了throws Exception,这时除了虚拟机没有其它方法能够引用main()方法,且在程序中可能看不到try…catch程序块,但并不会产生错误,因为此时虚拟机会捕捉"异常",并且会默认的调用printStackTrace()方法打印出"异常"路径。总之只要一个方法关联了"异常",可以将这个"异常"关联向上传递,但是最终必须使用catch来终止"异常",或者一直传递到main()方法交给Java虚拟机来结束"异常"对象的生命,否则是通不过编译的。