Java异常(Java核心技术卷一读书笔记)
1.异常分类
异常的对象都是派生于Throwable类的一个实例。
在throwable的下一层,立即分解为两个分支:Error,Exception
Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误。应用程序不应该抛出这种类型的对象。这种情况很少出现。
Exception层次又分解为两个分支:一个分支派生于RuntimeException;另一个分支包含其他异常。(由程序错误导致的异常输入RuntimeException;程序没问题,由于I/O错误这类问题导致属于其他异常)
派生于RuntimeException的异常包含下面几种情况:
- 错误的类型转换
- 数组访问越界
- 访问null指针
不是RuntimeException的异常包含:
- 试图在文件尾部后面读取数据
- 试图打开一个不存在的恩建
- 试图根据给定的字符串查找class对象,但是这个字符串表示的类并不存在
派生于Error类或者RuntimeException类的所有异常称为非受查异常(unchecked),其他所有异常称为受查异常(checked)
2.声明受查异常
在遇到以下情况时应该抛出异常:
- 在调用一个抛出 受查异常的方法,例如FileInputStream构造器
- 程序运行过程中发现错误,并利用throw语句抛出一个受查异常
- 程序出现错误,例如a[-1]会抛出一个ArrayIndexOutBoundsException这样的非受查异常
- Java虚拟机和运行库出现的内部错误
public FileInputStream(String name) throws FileNotFoundException//标准库中的声明
3.如何抛出异常
假设一个名为readData的方法正在读取一个首部具有下列的文件:
Content_length:1024
然而读到733个字符串之后文件就结束了,我们认为这是一种不正常的情况,希望抛出一个异常。
首先要决定一个抛出什么异常。第一反应是IOException,仔细读过Java api 文档之后会发现EOFException更合适(在输入过程中,遇到一个未预期的EOF后的信号)(EOF指end of file)
String readData(Scanner in) throws EOFException{ ... while(...){ if(!in.hasNext()){ if(n<len) throw new EOFException(); } } return s; }
对于一个已经存在的异常,只需要:
1.找到合适的异常类
2.创建这个类的对象
3.将对象抛出
4.创建异常类
在程序中,可能会遇到任何标志异常类都没有能够充分描述的问题。在这种情况下就只能创建自己的异常类了。
只需要定义一个派生于Exception的类,或者派生于Exception子类的类。习惯上,定义的类应该包含两个构造器,一个是默认的构造器,另一个是带有详细描述信息的构造器。
//自己定义的异常 class FileFormatException extends IOException{ public FileFormatException(){} public FileFormatException(String gripe){ super(gripe); } } String readData(BufferedReader in) throws FileFormatException{ ... while(...){ if(ch == 1){ if(n<len) throw new FileFormatException(); } } return s; }
5.捕获异常
如果某个异常发生的时候没有捕获,那程序就会终止执行,并在控制台打印出异常信息。
要想捕获异常,就要设置try/catch语句块。最简单的try/catch语句块如下:
try{ //code //more code }catch{ //handler for this type }
如果在try语句块中的任何代码抛出一个在catch子句中说明的异常类,那么
- 程序将跳过try语句块的其余代码
- 程序将执行catch子句中的处理代码
如果在try语句中没有抛出异常,就将跳过catch语句。
public void read(String filename){ try{ InputStream in = new FileInputStream(filename); //可能抛出IOException int b; while((b=in.read())!=-1){ //... } }catch(IOException exception){ exception.printStackTree(); } }
try{ //... }catch(FileNotFoundException e){ //... }catch(UnknownHostException e){ //... }catch(IOException e){ //... }
Finally子句
不论异常是否被捕获,finally子句中的代码都将被执行。例如在读取文件过程中抛出异常,需要关闭文件流。
InputStream in = new FileInputStream(...); try{ // 1 code that might throw exceptions // 2 }catch(IOException e){ // 3 show error message // 4 }finally{ // 5 in.close(); } //6
以上代码可能遇到3种情况:
1.代码没有抛出异常,将执行1,2,5,6处的代码
2.跑出一个catch捕获的异常:
a.如果catch抛出异常,将执行1,3,5 (为什么6不执行?因为这个异常在被抛出后,应该被返回给这个方法的调用 者。finally是必须执行的。情况3同理)
b.如果catch没有抛出异常,将执行1,2,4,5,6
3.代码抛出了一个catch不能捕获的异常,将执行1,5