抛出异常
异常分类
在Java中,异常对象都是派生于Throwable类的一个实例,下一层又立即分解为两个分支:Error和Exception。
Error类描述了Java运行时系统的内部错误和资源耗尽错误,应用程序不应该抛出这种类型的对象。这种情况很少出现。
Exception类又分为两个分支:由程序错误导致的异常RuntimeException和由于I/O错误导致的IOException。
RuntimeException异常包含以下几种情况:
- 错误的类型转换
- 数组访问越界
- 访问空指针
IOException异常包括:
- 试图在文件尾部后面读取数据
- 试图打开一个不存在的文件
- 试图根据给定的字符串查找Class对象,而它并不存在
IOException异常基本上是程序员自己失误造成的异常。
Java将派生于Error类或RuntimeException类的所有异常称为未检查异常。其他异常称为已检查异常。
一个方法必须声明所有可能抛出的已检查异常。而未检查异常要么不可控制(Error),要么就应该避免发生(RuntimeException)。
方法应该在其首部声明所有可能抛出的异常,如:
public FileInputStream(String name) throws FileNotFoundException
这个声明表示这个构造器将有可能抛出一个FileNotFoundException
异常。如果发生了这种情况,构造器将抛出异常,系统变开始搜索异常处理器,以便处理它。
当然,除了声明异常之外,还可以捕获异常。
如何抛出异常
假设在读取一个文件时,提前遇到了一个未预期的EOF,这时应该抛出一个EOFException异常。
String readData(Scanner in) throws EOFException {
...
while(...) {
if(!in.hasNext()) {//EOF encountered
if(n<len) throw new EOFException();
}
...
}
return s;
}
从上面可以看出,对于一个已经存在的异常类,将其抛出非常容易:
- 找到一个合适的异常类
- 创建这个类的一个对象
- 将对象抛出
捕获异常
如果一个异常发生时,没有在任何地方进行捕获,那么程序就会终止,并在控制台打印出异常信息。
要想捕获一个异常,必须设置try/catch 语句块,最简单的try语句如下:
try {
...
}
catch (ExceptionType e1) {
...
}
catch (ExceptionType e2) {
...
}
如果在try语句块中的任何代码抛出了一个在catch子句中说明的异常类,那么
- 程序将跳过try语句块中的其余代码
- 程序将执行catch子句中的处理器代码
如果在try语句块中没有抛出任何异常,那么程序将跳过catch子句。
再次抛出异常
catch语句还可以再抛出异常,例如:
try {
...
}
catch (SQLException e) {
Throwable se = new ServletException("database error");
se.initCause(e);
throw se;
}
finally子句
如果方法获得了一些本地资源,而且只有这个方法自己知道,又如果这些资源在退出方法之前必须被回收,那么就会产生资源回收问题。
Java中使用finally解决这个问题,不管是否有异常被捕获,finally子句都被执行:
InputStream in = new FileStream(...);
try {...}
catch(IOException e) {...}
finally {in.close;}
断言
断言机制允许在测试期间向代码中插入一些检查语句,当代码发布时,这些插入的检测语句将会被自动地移走。
Java语言引入了关键字assert,它有两种形式:
assert 条件;
和
assert 条件:表达式;
这两种形式都会对条件进行检测,如果结果为false,则抛出一个AssertionError异常。在第二种形式中,表达式将会被传入AssertionError的构造器,并转换成一个消息字符串。
例如,假设有
double y = Math.sqrt(x);
要想断言x是一个非负数值,只需要简单地使用下面这条语句:
assert x >= 0;
或者将x的实际值传递给AssertionError的构造器,从而显示出来:
assert x >= 0 : x;
启用和禁用断言
在默认情况下,断言被禁用,可以在运行程序时使用-enableassertions
或-ea
选项启用它:
java -enableassertions MyApp
使用断言完成参数检查
在Java中,给出了3种处理系统错误的机制:
- 抛出一个异常
- 日志
- 使用断言
什么时候使用断言呢?断言只应该用于在测试阶段确定程序内部的错误位置。