如果某个异常发生的时候没有在任何地方进行捕获,那程序就会终止执行,并在控制台上 打印出异常信息,其中包括异常的类型和堆栈的内容。对于图形界面程序(applet和application 应用程序),在捕获异常之后,也会打印出堆栈的信息,但程序将返回到用户界面的处理循环中。
要想捕获一个异常,必须设置try/catch语句块。最简单的try语句块如下所示:
try {
code
more code
more code
}
catch (ExcqjtionType e)
{
handler for this ti/pe
}
如果在try语句块中的任何代码抛出厂一个在catch 子句中说明的异常类,那么
1 )程序将跳过try语句块的其余代码。
2 )程序将执行catch子句中的处理器代码。
如果在try语句块中的代码没有抛出任何异常,那么程序将跳过catch 子句。
如果方法中的任何代码抛出了一个在catch子句中没有声明的样常类型,那么这个方法就会立刻退出(希望凋用者为这种类型的异常设计了 catch子句)
为了演示捕获异常的处理过程,下面给出一个读取文本的典型程序代码:
public void read(String filename)
{
try {
InputStreara in = new FilelnputStream(filename);
int b;
while ((b = in.readO) != -1)
{ process input }
}
catch (IOException exception)
{
excepti on.printStackTrace();
}
}
需要注意的是,try语句中的大多数代码邡很容易理解,读取并处理文本行,直到遇到文件结朿符为止。正如在Java API中看到的那样,read方法冇吋能抛出一个IOException异常。 在这种情况下,将跳出整个while循环,进人catch子句,并生成一个栈轨迹。对于一个普通的程序来说,这样处理异常基本上合乎情理。还有其他的选杼吗?
通常,最好的选择是什么也不做,而是将异常传递给调用者。如果read方法出现了错误,就让read方法的调用者去操心!如果采用这种处理方式,就必须声明这个方法可能会抛 出一个 IOException。
public void read(String filename) throws IOException
{
InputStreara in = new FilelnputStrean(filenane);
int b;
while ((b = in.readQ) != -1)
{process input}
}
记住,编译器严格地执行thmws说明符。如果调用了一个抛出已检查异常的方法,就必须对它进行处理,或者将它继续进行传递。
哪种方法更好呢?通常,应该捕获那些知道如何处理的异常,而将那些不知道怎样处理的异常继续进行传递。如果想传递一个异常,就必须在方法的首部添加一个throws说明符, 以便告知调用者这个方法可能会拋出异常。
仔细阅读一下Java API文档,以便知道每个方法可能会拋出哪种异常,然后再决定是自己处理,还是添加到throws列表中。对于后一种情况,也不必犹豫。将异常直接交给能够胜任的处理器进行处理要比压制对它的处理更好。
同时请记住,这个规则也有一个例外。如果编写一个指覆盖超类的方法, 而这个方法又没有抛出好常(如JComponent中的paintComponent),那么这个方法就必须捕获方法代码中出现的每一个已检查异常。不允许在子类的throws说明符中出现超过超类方法所列出的异常类范围。
在一个try语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理。可以按照下列方式为每个异常类型使用一个单独的catch子句:捕获多个异常
try {
code that might throw exceptions
}
catch (FileNotFoundException e)
{
emergency action for missing files
}
catch (UnknownHostException e)
{
emergency action for unknown hosts
}
catch (IOException e) {
emergency action for all other I/O problems
}
异常对象可能包含与异常本身有关的信息。要想获得对象的更多信息,可以试着使用
e.getMessage()
得到详细的错误信息(如果有的话),或者使用
finally 子句e.getClass().getName()得到异常对象的实际类型。
当代码抛出一个异常时,就会终止方法中剩余代码的处理,并退出这个方法的执行。如果方法获得了一些本地资源,并且只有这个方法自己知道,又如果这些资源在退出方法之前必须被回收,那么就会产生资源回收问题。一种解决方案是捕获并重新拋出所有的异常。但是,这种解决方案比较乏味,这是因为需要在两个地方清除所分配的资源,一个在正常的代 码中;另一个在异常代码中。
Java存一种更好的解决方案,这就是finally子句。下而将介绍Java中如何恰当地关闭一 个文件。如果使用Java编写数据库程序,就需要使用同样的技术关闭与数据库的连接。
不管是否有异常被捕获,finally子句中的代码都被执行。在下面的示例中,程序将在所有情况下关闭文件。
InputStream in = new FileInputStream(...);
try {
//I
code that might throw exceptions
//2
} catch (IOException e)
{
// 3
show error message
//4
}
finally {
// 5
in.close();
}
//6
在上面这段代码中,有下列3种情况会执行finally子句:
1) 代码没有抛出异常。在这种情况下,程序首先执行try语句块中的全部代码,然后执行finally子句中的代码。随后,继续执行try语句块之后的第一条语句。也就是说,执行标注的1、2、5、6处。
2) 抛出一个在catch子句中捕获的异常。在上面的示例中就是lOException异常。在这种情况下,程序将执行try语句块中的所有代码,直到发生异常为止。此时,将跳过try语句块中的剩余代码,转去执行与该异常匹配的catch子句中的代码,最后执行finally子句中的代码。
如果catch子句没有拋出异常,程序将执行try语句块之后的第一条语句。在这里,执行标注1、3、4、5、6处的语句。
如果catch子句抛出了一个异常,异常将被拋回这个方法的调用者。在这里,执行标注 1、3、5处的语句。
3) 代码拋出了一个异常,但这个异常不是由catch子句捕获的。在这种情况下,程序将执行try语句块中的所有语句,直到有异常被抛出为止。此时,将跳过try语句块中的剩余代码,然后执行finally子句中的语句,并将异常拋给这个方法的调用者。在这里,执行标注1、 5处的语句。