private void cleanupConnections() throws ExceptionOne, ExceptionTwo {
for (int i = 0; i < connections.length; i++) {
connection[i].release(); // Throws ExceptionOne, ExceptionTwo
connection[i] = null;
}
connections = null;
}
protected abstract void cleanupFiles() throws ExceptionThree, ExceptionFour;
protected abstract void removeListeners() throws ExceptionFive, ExceptionSix;
public void cleanupEverything() throws Exception {
cleanupConnections();
cleanupFiles();
removeListeners();
}
public void done() {
try {
doStuff();
cleanupEverything();
doMoreStuff();
}
catch (Exception e) {}
}
在其它地方的某段代码中,数组connections在第一个连接被建立时初始化。但是如果没有一个被建立,那么connections将是空的。所以在某种情况下,语句connections[i].release()的调用结果将会引发一个NullPointerException异常。这是一个相当容易修复的问题。仅仅添加一个检查语句“connections!=null”即可。
然而,有一个异常永远都不会被报告,它先被cleanupConnections()抛出,接着又被cleanupEverything()抛出,最后在done()中被捕获。done()方法的异常处理没有做任何事情,甚至没有日志记录。因为cleanupEverything()仅通过done()被调用,这个异常因此永远不会被看到。所以代码也永远不会被修复。
这样的话,假设现在cleanupConnections()执行失败,那么cleanupFiles()和removeListeners()方法永远都不会被调用(资源也就永远不会释放),并且doMoreStuff()也从不会被调用,这样在done()中的最后处理永远不会完成。为了使事情更糟,在系统关闭时done()不被调用,相反在完成每个事务时被调用,因而资源泄漏会出现在每个事务中。
问题很明显,主要是:错误没有被报告和资源泄漏。但是代码本身似乎没有错误,并且从代码编写方式中,这个问题增加了跟踪的难度。然而,通过应用一些简单的指导原则,这个问题就会被找出和修复:
不要忽略异常
不要捕获泛型的Exceptions
不要抛掷泛型Exceptions
不要忽略异常
在代码清单1中最明显的问题是在程序中的一个错误被完全忽略。一个非预期的异常(异常天生就是非预期的)被抛出,并且代码也没有打算处理那个异常。这个异常甚至没有被报告因为代码假设预期的异常并不重要。
在大多数情况下,一个异常至少应该被记录。有几个日志包(请看“Logging Exceptions”)可以记录系统错误和对系统没有重大影响的异常。
大多数日志系统也允许栈记录被打印,这样就提供了在哪和为什么会发生异常的有价值的信息。最后,因为一般情况下日志都被写到文件中,所以一条异常记录能被查看和分析。你可以看一下在“Logging Exceptions的代码清单11中有一个记录堆栈跟踪的例子。
在某些特殊情况下,日志异常并不是关键的,比如在finally子句中的资源清理是其中之一。
finally中的异常(Exceptions)
在下面的代码清单2中,需要从一个文件中读取数据。无论在读数据时是否发生异常文件都需要关闭,所以close()方法被放在finally子句中。但是如果在关闭文件时再发生错误,那就无能为力了。
代码清单2:
public void loadFile(String fileName) throws IOException {
InputStream in = null;
try {
in = new FileInputStream(fileName);
readSomeData(in);
}
finally {
if (in != null) {
try {
in.close();
}
catch(IOException ioe) {
// Ignored
}
}
}
}
注意如果由于I/O的问题而使数据装入失败,那么对loadFile()方法的调用仍然会抛出一个IOException异常。另外一点,即使异常在close()中被忽略,代码的声明(在注释语句行)也使它对任何使用它的人清晰易读。你可以应用这种方式去清理任何I/O流、关闭socket和JDBC连接等等。
在忽略异常时关键是要确保在要忽略的try/catch块只有一个方法(这样别的方法才可能在其之外被调用)并且有一个指定的异常被捕获。这种特殊的情况与捕获一个普通的Exception有明显的不同。在所有其它的情况下,异常应该(至少)被记入日志,其内容用堆栈跟踪记录很合适。
未完待续。
本文地址:http://com.8s8s.com/it/it16967.htm