【java基础(四十六)】异常(二)声明异常、抛出异常、创建异常

声明受查异常

如果遇到了无法处理的情况,那么Java的方法可以抛出一个异常。这个道理很简单:一个方法不仅需要告诉编译器将要返回什么值,还要告诉编译器有可能发生什么错误。例如:一段读取文件的代码知道有可能读取的文件不存在,或者内容为空,因此,试图处理文件信息的代码就需要通知编译器可能会抛出IOException类的异常。

方法应该在其首部声明所有可能抛出的异常。这样可以从首部反映出这个方法可能抛出哪类受查异常。例如,下面是标准类库中提供的FileInputStream类的一个构造器的声明。

public FileInputStream(String name) throws FileNotFoundException

这个声明表示这个构造器将根据给定的String参数产生一个FileInputStream对象,但也有可能抛出一个FileNotFoundException异常。如果发生了这种糟糕的情况,构造器将不会初始化一个新的FileInputStream对象,而是抛出一个FileNotFoundException类对象。如果这个方法真的抛出了这样一个异常对象,运行时系统就会开始搜索异常处理器,以便知道如何处理FileNotFoundException对象。

在自己编写方法时,不必将所有可能抛出的异常都进行声明。至于什么时候需要在方法中用throws子句声明异常,什么异常必须使用throws子句声明,需要记住在遇到下面4中情况时应该抛出异常:

  • 调用一个抛出受查异常的对象,例如,FileInputStream构造器。
  • 程序运行过程中发现错误,并且利用throw语句抛出一个受查异常。
  • 程序出现错误,例如,a[-1] = 0会抛出一个ArrayIndexOutOfBoundsException这样的非受查异常。
  • Java虚拟机和运行时库出现的内部错误。

如果出现前两种情况之一,则必须告诉调用这个方法的程序员有可能抛出异常。为什么?因为任何一个抛出异常的方法都有可能是一个死亡陷阱。如果没有处理器捕获这个异常,当前执行的线程就会结束。

对于那些可能被他人使用的Java方法,应该根据异常规范(exception specification),在方法的首部声明这个方法可能抛出的异常。

class MyAnimation {

	public Image loadImage(String s) throws IOException {
		...
	}

}

如果一个方法有可能抛出多个受查异常类型,那么就必须在方法的首部列出所有的异常类。每个异常类之间用逗号隔开。如:

class MyAnimation {

	public Image loadImage(String s) throws FileNotFoundException, EOFException {
		...
	}

}

但是,不需要声明Java的内部错误,即从Error继承的错误。任何程序代码都具有抛出那些异常的潜能,而我们对其没有任何控制能力。

同样,也不应该声明从RuntimeException继承的那些非受查异常。如:

class MyAnimation {

	void drawImage(int i) throws ArrayIndexOutOfBoundsException { // 这样是不对的
		...
	}
}

这些运行时错误完全在我们的控制之下。如果特别关注数组下标引发的错误,就应该将更多的时间花费在修正程序中的错误上,而不是说明这些错误发生的可能性上。

总之,一个方法必须声明所有可能抛出的受查异常,而非受查异常要么不可控制(Error),要么就应该避免发生(RuntimeException)。如果方法没有声明所有可能发生的受查异常,编译器就会发出一个错误消息。

当然,从前面的示例中可以知道:除了声明异常之外,还可以捕获异常。这样会使异常不被抛到方法之外,也不需要throws规范。稍后,将会讨论如何决定一个异常是被捕获,还是被抛出让其他的处理器进行处理。

如果类中的一个方法声明将会抛出一个异常,而这个异常是某个特定类的实例时,则这个方法将有可能抛出一个这个类的异常,或者这个类的任意一个子类的异常。例如,FileInputStream构造器声明将有可能抛出一个IOException异常,然而并不知道具体是哪种IOException异常。它既可能是IOException异常,也坑你是其子类的异常。如:FileNotFoundException。

抛出异常

假设在程序代码中发生了一些很糟糕的事情。一个名为readData的方法正在读取一个首部具有下列信息的文件:
Content-length: 1024
然而,读到733个字符之后文件就结束了。我们认为这是一种不正常的情况,希望抛出一个异常。

首先要决定应该抛出什么类型的异常。将上述异常归结为IOException是一种很好的选择。仔细地阅读Java API文档之后会发现:EOFException异常描述的是“在输入过程中,遇到了一个未预期的EOF后的信号”。这正是我们要抛出的异常。下面是抛出这个异常的语句:

throw new EOFException();

或者

EOFException e = new EOFException();
throw e;

和代码放在一起:

String readData(Scanner in) throws EOFException {
	while (...) {
		if (!in.hasNext()) {
			if (n < len)
				throw new EOFException();
		}
	}
}

EOFException 类还有一个含有一个字符串型参数的构造器。这个构造器可以更加细致的描述异常出现的情况。

String gripe = "Content-length:" + len + ", Receive:" + n;
throw new EOFException(gripe);

在前面已经看到,对于一个已经存在的异常类,将其抛出非常容易。

  • 找到一个合适的异常类。
  • 创建这个类的一个对象。
  • 将对象抛出。

一旦方法抛出了这个异常,这个方法就不可能返回到调用者。也就是说,不必为返回的默认值或错误代码担忧。

创建异常类

在程序中,可能会遇到任何标准异常类都没有能够充分地描述清楚的问题。在这种情况下,创建自己的异常类就是一件顺利成章的事情了。我们需要做的只是定义一个派生于Exception的类,或者派生于Exception子类的类。例如,定义一个派生于IOException的类。习惯上,定义的类应该包含两个构造器,一个是默认的构造器;另一个是带有详细描述信息的构造器(超类Throwable的toString方法将会打印出这些详细信息,这在调试中非常有用)。

class FileFormatException extends IOException {
	public FileFormetExcetpion() {};
	public FileFormatExcetpin(String gripe) {
		super(gripe);
	}
}

现在,就可以抛出自己定义的异常类型了。

String readData(BufferedReader in) throws FileFormatException {
	while (...) {
		if (ch == -1) {
			if (n < len)
				throw new FileFormatException();
		}
	}
}

捐赠

若你感觉读到这篇文章对你有启发,能引起你的思考。请不要吝啬你的钱包,你的任何打赏或者捐赠都是对我莫大的鼓励。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章