Java学习总结之第八章 异常处理

一、Java异常处理机制概述

1、Java虚拟机用方法调用栈来跟踪每个线程中一系列的方法调用过程。

2、方法中的代码块可能抛出异常,有如下两种处理办法:

l 在当前方法中通过try…catch语句捕获并处理异常。例如:

public void methodA(int money){

try{

//以下代码可能会抛出SpecialException

if(--money <= 0)

throw new SpecialException("Out of money");

}

catch(SpecialException e){

//异常处理

}

}

l 在方法的声明处通过throws语句声明抛出异常,返回给调用者来处理异常例如:

由于方法A不能捕获异常,所以,A就要通过throws语句声明抛出异常,由调用者methodB()来处理异常

public void methodA(int money)throws SpecialException{

//以下代码会抛出SpecialException

if(--money <= 0)

throw new SpecialException("Out of money");

}

此时methodB()的定义如下:

public void methodB(int money){

try{

methodA(money);

}

catch(SpecialException e){

//处理异常

}

}

如果methodB()也没有捕获SpecialException,而是声明抛出该异常,则Java虚拟机的处理流程将退回到methodB()方法的调用者,此时methonB()方法的定义如下:

public void methodB(int money)throws SpecialException{

methodA(money);

}

3、当Java虚拟机追溯到调用栈的底部的方法时,如果仍然没有找到处理该异常的代码块,将按以下步骤处理:

l 调用异常对象的printStackTrace()方法,打印来自方法调用栈的异常信息。

l 如果该线程不是主线程,那么终止这个线程,其他线程继续正常运行。如果该线程是主线程(即方法调用栈的底部为main()方法),那么整个应用程序被终止。

二、运用Java异常处理机制

1、在Java语言中,用try…catch语句来捕获异常,格式如下:

try{

//可能会出现异常的代码

}

catch(SQLException e){

//处理操纵数据库出现的异常

}

catch(IOException e){

//处理操纵输入流和输出流出现的异常

}

2、finally语句中的代码是在异常处理的任何情况下都必须执行的代码。finally代码块能保证特定的操作总是会被执行,它的形式如下:

try{

//可能会出现异常的代码

}

catch(Exception e){

//处理异常

}

finally{

//特定的操作

}

3、不管try代码块中是否出现异常,都会执行finally代码块。

4、如果一个可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。一个方法可能会出现多种异常,throws子句允许声明抛出多个异常,例如:

public void method()throws SQLException,IOException{}

5、throw语句用于抛出异常,由throw语句抛出的对象必须是java.lang.Throwable类或者其子类的实例。

6、异常处理语句的语法规则:

l try代码块不能脱离catch代码块或finally代码块而单独存在。try代码块后面至少有一个catch代码块或finally代码块。

l try代码块后面可以有零个或多个catch代码块,还可以有零个或至多一个finally代码块。如果catch代码块和finally代码块并在,finally代码块必须在catch代码块后面。

l try代码块后面可以只跟finally代码块。

l 在try代码块中定义的变量的作用域为try代码块,在catch代码块和finally代码块中不能访问该变量。

l 当try代码块后面有多个catch代码块时,Java虚拟机会把实际抛出的异常对象依次和各个catch代码块声明的异常类型匹配,如果异常对象为某个异常类型或其子类的实例,就执行这个catch代码块,而不会再执行其他的catch代码块。

l 如果一个检查异常,要么用try…catch语句捕获,要么用throws语句声明将它抛出,否则会导致编译错误。

l throws语句后面不允许紧跟其他语句,因为这些语句永远不会被执行。

7、异常流程的运行过程:

l finally语句不会被执行的惟一情况是先执行了用于终止程序的System.exit()方法。exit()方法的定义如下:public static void exit(int status) ,exit()方法的参数status表示程序终止时的状态码,按照编程惯例,0表示正常终止,非零数字表示异常终止。

l return语句用于退出本方法。在执行try或catch代码块中的return语句时,假如有finally代码块,会先执行finally代码块。

l finally代码块虽然在return语句之前被执行,但finally代码块不能通过重新给变量赋值的方式来改变return语句的返回值。

l 建议不要在finally代码块中使用return语句,因为它会导致两种潜在的错误。第一种错误是覆盖try或catch代码块的return语句,第二种错误是丢失异常。

三、Java异常类

1、所有异常类的祖先类为java.lang.Throwable类,它的实例表示具体的异常对象,可以通过throw语句抛出,Throwable类提供了访问异常信息的一些方法,常用的方法包括:

l getMessage()——返回String类型的异常信息。

l printStackTrace()——打印跟踪方法调用栈而获得的详细异常信息。在程序调试阶段,此方法可用于跟踪错误 。

2、常见的运行时异常包括:NullPointerException、ClassCastException、ArithmeticException、IllegalArgumentException和IndexOutOfBoundsException,这种异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常时,即使没有用try…catch语句捕获它,也没有用throws子句声明它,还是会通过编译。

四、用户定义异常

1、异常转译

当位于最上层的子系统不需要关心来自底层的异常的细节时,常见的做法是捕获原始的异常,把它转换为一个新的不同类型的异常,再抛出新的异常,这种处理异常的办法称为异常转译。示例代码如下:

public void uploadImageFile(String imagePath)throws UpLoadException{

try{

//上传图像

}catch(IOException e){

//把原始异常信息记录到日志中,便于排错

throw new UpLoadException();

}catch(SQLException e){

//把原始异常信息记录到日志中,便于排错

throw new UpLoadException();

}

}

2、异常链

JDK1.4以上版本中的Throwable类支持异常链机制。所谓异常链就是把原始异常包装为新的异常类,也就是说在新的异常类中封装了原始异常类,这有助于查找产生异常的根本原因。如果使用JDK1.4以下的版本,可以由开发者自行设计支持异常链的异常类,以下示例代码提供了一种实现方案,JDK1.4以上版本中的Throwable类的实现机制与它很相似。

import java.io.*;

public class BaseException extends Exception{

protected Throwable cause = null;

public BaseException(){}

public BaseException(String msg){super(msg);}

public BaseException(Throwable cause){

this.cause = cause;

}

public BaseException(String msg,Throwable cause){

super(msg);

this.cause = cause;

}

public Throwable initCause(Throwable cause){

this.cause = cause;

return this;

}

public Throwable getCause(){

return cause;

}

public void printStackTrace(){

printStackTrace(System.err);

}

public void printStackTrace(PrintStream outStream){

printStackTrace(new PrintWriter(outStream));

}

public void printStackTrace(PrintWriter writer){

super.printStackTrace(writer);

if(getCause() != null){

getCause().printStackTrace(writer);

}

writer.flush();

}

}

在BaseException中定义了Throwable类型的cause变量,它用于保存原始的Java异常。假定UploadException类扩展了BaseException类:

public class UploadException extends BaseException{

public UploadException(Throwable cause){super(cause);}

public UploadException(String msg){super(msg);}

}

以下是把IOException包装为UploadException的代码。

try{

//上传图像文件

}catch(IOException e){

//把原始异常信息记录到日志中,便于排错

//把异常类包装为UploadException

UploadException ue = new UploadException(e);

throw ue;

}

3、处理多样化异常

Java方法中一次只能抛出一个异常对象,如果需要抛出多个异常,开发者必须自行设计支持多样化异常的异常类,以下为一种实现方案的示例代码:

import java.util.List;

import java.util.ArrayList;

import java.io.PrintStream;

import java.io.PrintWriter;

public class BaseException extends Exception{

protected Throwable cause = null;

private List<Throwable> exceptions = new ArrayList<Throwable>();

public BaseException(){}

public BaseException(String msg){super(msg);}

public BaseException( Throwable cause ) {

this.cause = cause;

}

public BaseException(String msg,Throwable cause){

super(msg);

this.cause = cause;

}

public List getExceptions() {

return exceptions;

}

public void addException( BaseException ex ){

exceptions.add( ex );

}

public Throwable initCause(Throwable cause) {

this.cause = cause;

return this;

}

public Throwable getCause() {

return cause;

}

public void printStackTrace() {

printStackTrace(System.err);

}

public void printStackTrace(PrintStream outStream) {

printStackTrace(new PrintWriter(outStream));

}

public void printStackTrace(PrintWriter writer) {

super.printStackTrace(writer);

if ( getCause() != null ) {

getCause().printStackTrace(writer);

}

writer.flush();

}

}

BaseException类包含了一个List类型的exceptions变量,用来存放其他的Exception。以下代码显示了BaseException的用法。

public void check()throws BaseException{

BaseException be = new BaseException();

try{

checkField1();

}catch(Field1Exception e){

be.addException(e);

}

try{

checkField2();

}catch(Field2Exception e){

be.addException(e)

}

if(be.getExceptions().size() > 0)

throw be;

}

五、异常处理原则

1、异常处理只能用于非正常情况。

2、为异常提供说明文档。通过JavaDoc的@throws标签来描述产生异常的条件。

3、尽可能地避免异常。

4、保持异常的原子性。异常的原子性是指当异常发生时后,各个对象的状态能够恢复到异常发生前的初始状态,而不至于停留在某个不合理的中间状态。保持原子性有以下办法:

l 最常见的办法是先检查方法的参数是否有效,确保当异常发生时还没有改变对象的初始状态。

l 编写一段恢复代码,由它来解释操作过程中发生的失败,并且使对象状态回滚到初始状态。这种办法不是很常用,主要用于永久性的数据结构,比如数据库系统的回滚机制就采用了这种办法。

l 在对象的临时拷贝上进行操作,当操作成功后,把临时拷贝中的内容复制到原来的对象中。

5、避免过于庞大的try代码块。

6、在catch子句中指定具体的异常类型

7、不要在catch代码块中忽略被捕获的异常。

posted @ 2009-10-14 11:57  陆止于此 海始于斯  阅读(2895)  评论(0编辑  收藏  举报