Java异常(Java核心技术卷一读书笔记)

时间:2022-01-19 00:44:35

1.异常分类

异常的对象都是派生于Throwable类的一个实例。

Java异常(Java核心技术卷一读书笔记)


在throwable的下一层,立即分解为两个分支:Error,Exception


Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误。应用程序不应该抛出这种类型的对象。这种情况很少出现。

Exception层次又分解为两个分支:一个分支派生于RuntimeException;另一个分支包含其他异常。(由程序错误导致的异常输入RuntimeException;程序没问题,由于I/O错误这类问题导致属于其他异常)


派生于RuntimeException的异常包含下面几种情况:

  1. 错误的类型转换
  2. 数组访问越界
  3. 访问null指针
不是RuntimeException的异常包含:
  1. 试图在文件尾部后面读取数据
  2. 试图打开一个不存在的恩建
  3. 试图根据给定的字符串查找class对象,但是这个字符串表示的类并不存在

派生于Error类或者RuntimeException类的所有异常称为非受查异常(unchecked),其他所有异常称为受查异常(checked)


2.声明受查异常

在遇到以下情况时应该抛出异常:

  1. 在调用一个抛出 受查异常的方法,例如FileInputStream构造器
  2. 程序运行过程中发现错误,并利用throw语句抛出一个受查异常
  3. 程序出现错误,例如a[-1]会抛出一个ArrayIndexOutBoundsException这样的非受查异常
  4. 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子句中说明的异常类,那么

  1. 程序将跳过try语句块的其余代码
  2. 程序将执行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语句中可以捕获多个异常,并对不同的异常作出不同的处理
                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