异常与断言
在理想世界里,用户输入数据的格式永远都是正确的,代码永远不会出现bug,然而,在现实世界的开发过程中,总会遇到各种各样的问题,当然,我也可以说,这不是bug,而是特性~~~
言归正传,如果由于程序的错误导致用户所做的工作统统丢失,那用户可能就再也不会用它了。为了避免这样的情况,至少应该做到以下几点向用户通知错误
保存所有的工作
允许用户妥善地退出程序
1. 处理错误
为了能够处理程序中的异常情况,必须考虑到程序中可能会出现的错误和问题。比如:用户输入错误
设备错误
物理限制
代码错误
1.1 异常分类
在Java中,异常对象都是派生于Throwable类的一个实例。在下一层分解为两个分支,Error和Exception。 Error描述了Java运行时系统的内部错误和资源耗尽错误 Exception又分解为两个分支 由编程错误导致的RuntimeException,比如错误的强制类型转换,数组访问越界等 由I/O错误导致的其他异常IOException,比如试图打开一个不存在的文件等 如下图所示是异常层次结构的一个示意图
Tip如果出现RuntimeException,那么一定是你的问题
Java将派生于Error类或RuntimeException类的所有异常称为非检查型(unchecked),所有其他异常称为检查型(checked)
1.2 如何抛出异常
假设我们正在读取一个文件,预期读取1024个字符,但是在读到500个字符的时候文件就结束了,出现了问题,我们希望抛出一个EOFException,即输入过程中意外遇到了EOF。
String readData(Scanner in) throws EOFException {
// blablabla if (!in.hasNext()) {
if (n < len) {
throw new EOFException();
}
}
}
1.3 创建异常类
如果我们遇到了标准异常类无法描述清楚的问题,可以自己创建异常类。我们可以定义一个派生于Exception的类,或者某个Exception的子类,比如IOException。
class FileFormatException extends IOException {
public FileFormatException() {}
public FileFormatException(String gripe) {
super(gripe);
}
}
2. 捕获异常
要想捕获一个异常,需要设置try/catch块。
try {
// blablabla} catch (Exception e) {
// blablabla}
如果try语句块中的代码抛出了catch子句中指定的一个类,那么程序将跳过try语句块中的其余代码,并执行catch语句块中的代码。如果try中的代码没有异常,那么程序就会跳过catch语句。
2.1 finally子句
代码抛出一个异常时,就会停止处理这个方法中的剩余代码,但是如果这个方法中存在一些资源需要被清理,就会产生问题。 finally子句不管异常有没有被捕获,都会执行。比如下面的例子,所有情况下都会程序都会关闭输入流。
FileInputStream in = new FileInputStream();
try {
// blablabla}
catch (IOException e) {
// blablabla}
finally {
in.close();
}
2.2 try-with-resources语句
对于文件操作IO流、数据库连接等开销非常昂贵的资源,用完之后必须及时通过close方法将其关闭,否则资源会一直处于打开状态,可能会导致内存泄露等问题。关闭资源的常用方式就是在finally块里是释放,即调用close方法。就像我们在2.1节中的例子一样,但是从java7开始,我们可以使用一种更好的方法try-with-resources语句来实现。它的形式如下
try (Resources res = ...) {
// blablabla}
我们来看一个具体的例子,读取一个文件中的所有单词。
try (Scanner in = new Scanner(
new FileInputStream("/usr/share/dict/words"), StandardCharsets.UTF_8)) {
while (in.hasNext()) {
System.out.println(in.next());
}
}
3. 使用断言
断言机制允许在测试期间向代码中插入一些检查,而在生产代码中会自动删除这些检查。断言的关键字是assert。有以下两种形式
assert condition;
assert condition : expression;
这两种形式都会计算条件,如果结果为false,抛出AssertionError异常。 在第二个语句中,表达式将传入AssertionError对象的构造器,并转换成一个消息字符串。
默认情况下,断言是禁用的,需要在运行程序时用-ea启用断言。
参考:《Java核心技术 卷I》