理解异常一
一.异常简介
Exception是"Exception Event"的简称.
当某个方法中产生了一个错误,这个方法将产生一个对象并传递给运行时系统.这个对象就叫做异常对象,它包含了错误的信息,它的类型以及发生错误时程序的运行状态.创建一个对象并把它传递给运行时系统的行为就叫做抛出异常(throwing an exception).
当异常发生后,运行时系统试图找到一个合适的异常处理器来处理这个异常.如何找呢?方法就是从异常发生的地方反向搜索调用栈(call stack),直到找到一个合适的处理器,那么我们说捕获了这个异常(catch the exception).什么才是一个合适的处理器呢?只要发生的异常与Catch块中声明的异常类型相同或者是其父类型,我们就说它是一个合适的处理器.如果在调用栈(call stack)中没有搜索到合适的异常处理器,运行时系统将会终止.
二.异常分类
第一种就是checked exception,是可预知的各种情况以及能迅速从错误中恢复过来的.例如,想象一个应用程序提示用户输入文件名,然后根据文件名构造一个java.io.FileReader对象,正常情况下,用户输入的是已经存在的并且可读的文件名,程序将会正常执行.然而当用户输入了一个错误的,不存在的文件名时,程序将会抛出一个java.io.FileNotFoundException 一个良好的程序就会提示出错了,让用户输入正确的文件名.
除了被Error,RuntimeException和它们的子类指明的,其余的Exception都是checked exception.是应用程序可以处理的异常.
第二种异常就是error,是应用程序不能预先知道并且恢复的.例如,当某个程序打开了一个文件作为输入,但是由于硬件或者操作系统出现故障,程序不能获取输入,那么将会抛出一个java.io.IOError.凡是被Error或其子类指定的就是error.
第三种就是runtime exception,就是那些程序不能预知的的内部错误.比如说程序的bugs,逻辑上的错误,或者错误的使用了API等.凡是被RuntimeException或其子类指定的就是runtime exception.
error和runtime exception都叫做unchecked exception.
三.捕获和处理异常
这个部分讲解如何使用异常处理组件(try,catch,finnally)来编写一个异常处理器.
下面的例子定义了一个ListOfNumbers类.在构造方法中创建了一个ArrayList对象,用于存放一个从0-9的数字列表.并定义了一个writeList方法,用于将数字列表输出到一个叫OutFile.txt的文本文件中.
//Note: 这个类故意被设计成不能被编译的 import java.io.*; import java.util.List; import java.util.ArrayList; public class ListOfNumbers { private List<Integer> list; private static final int SIZE = 10; public ListOfNumbers () { list = new ArrayList<Integer>(SIZE); for (int i = 0; i < SIZE; i++) { list.add(new Integer(i)); } } public void writeList() { PrintWriter out = new PrintWriter( new FileWriter("OutFile.txt")); for (int i = 0; i < SIZE; i++) { out.println("Value at: " + i + " = " + list.get(i)); } out.close(); } }
new FileWriter("OutFile.txt"));
这行代码调用了一个构造方法用于初始化一个输出流,如果文件不能被打开,这个构造方法将会抛出一个
IOException.
out.println("Value at: " + i + " = " + list.get(i));
这行代码调用了ArrayList的get方法,如果传给它的参数太小或者太大都会抛出一个IndexOutOfBoundsException.
如果你想编译这个类,编译器将会打印出关于FileWriter抛出的异常信息,而不会打印get方法抛出的异常信息.原因是IOException是一个checked exception,
IndexOutOfBoundsException
是一个unchecked exception.
try块:
第一步就是将可能抛出异常的代码封装在一个try块中.
try{
code
}
catch块和finnally块
你可以在每行可能抛出异常的代码外加上一个try,catch块,也可以在多行中只使用一个try,catch块.本例中使用的第二种方式.
private List<Integer> list; private static final int SIZE = 10; PrintWriter out = null; try { System.out.println("Entered try statement"); out = new PrintWriter(new FileWriter("OutFile.txt")); for (int i = 0; i < SIZE; i++) { out.println("Value at: " + i + " = " + list.get(i)); } } catch and finally statements . . .
catch块:
通过直接在try块之后增加一个或者多个catch块来与try块进行关联,在try块和第一个catch块之间不能有任何其他代码.
try { } catch (ExceptionType name) { } catch (ExceptionType name) { }
没一个catch块都是一个异常处理器,用来处理其参数指明的异常类型或其子类型.
如果异常处理器被调用,改catch块中的代码将会执行.
下面是两个writeList方法的两个异常处理器
try { } catch (FileNotFoundException e) { System.err.println("FileNotFoundException: " + e.getMessage()); throw new SampleException(e); } catch (IOException e) { System.err.println("Caught IOException: " + e.getMessage()); }
两个异常处理器都打印错误消息,第二个出了这个什么也不做,只是让程序继续执行.第一个处理器打印错误消息并抛出一个用户自定义异常.在这个例子中,如果FileNotFoundException被捕获将会倒值SampleException被抛出,如果你想让你的程序在这种情况下用一种特定的方式处理,那么你也可以这么做.
异常处理器不仅仅能打印错误信息和终止程序,还可以从错误中恢复,提示用户,或者通过exception链传到更高级别的处理器去处理.
在一个处理器中捕获多个异常:
在java7中,一个catch块中可以处理不止一种异常.这个特性使得我们可以减少很多重复代码.多个exception用
"|"分隔.
catch (IOException|SQLException ex) { logger.log(ex); throw ex; }
注意:如果使用这种方式,ex将是final的,你不能在catch块中给它赋值.