Java IO:异常处理
使用流和读取器/写入器时,需要正确关闭它们。这是通过调用close()方法完成的。不过,这需要一点思考。看下面的代码:
InputStream input = new FileInputStream("c:\data\input-text.txt"); int data = input.read(); while(data != -1) { //do something with data... doSomethingWithData(data); data = input.read(); } input.close();
乍一看,这段代码看起来还不错。但是,如果从doSomethingWithData()方法内部抛出异常,会发生什么情况呢?这是正确的! InputStream永远不会关闭!
为了避免这种情况,我们可以将代码重写为此:
InputStream input = null; try{ input = new FileInputStream("c:\data\input-text.txt"); int data = input.read(); while(data != -1) { //do something with data... doSomethingWithData(data); data = input.read(); } }catch(IOException e){ //do something with e... log, perhaps rethrow etc. } finally { if(input != null) input.close(); }
注意,如何在finally子句中关闭" InputStream"。无论try块内发生了什么,都将执行finally子句。因此," InputStream"将始终关闭。
但是,如果close()抛出异常怎么办?说流已经关闭了吗?好了,要解决这种情况,我们也必须将对close()的调用也包装在try-catch块中,如下所示:
} finally { try{ if(input != null) input.close(); } catch(IOException e){ //do something, or ignore. } }
一旦正确地处理了异常,则正确处理" InputStream"(或者OutputStream)迭代的代码就可能很难看。这种丑陋的异常处理代码并不是特别好,因为它已经遍及整个代码,并一遍又一遍地重复。如果有人急着走开一个角落并跳过异常处理该怎么办?
此外,想象一下,首先从doSomethingWithData()抛出异常。第一个catch
子句将捕获该异常,然后finally子句将尝试关闭InputStream
。但是,如果input.close()方法也抛出异常,该怎么办?应当在调用堆栈中传播这两个异常中的哪一个?
幸运的是,有办法解决这个问题。该解决方案称为"异常处理模板"。创建一个异常处理模板,该模板在使用后正确关闭流。该模板只编写一次,并在整个代码中重复使用。漂亮又简单。要了解更多信息,请转到Java中的异常处理模板。
Java 7的Java IO异常处理
从Java 7开始,Java包含一种新的异常处理机制,称为"尝试使用资源"。当我们使用的资源在使用后需要正确关闭时,例如InputStream
,OutputStream
等,此异常处理机制特别针对处理异常处理。