Tips
《Effective Java, Third Edition》一书英文版已经出版,这本书的第二版想必不少人都读过,号称Java四大名著之一,不过第二版2009年出版,到如今已经将近8年的时间,但随着Java 6,7,8,甚至9的发布,Java语言发生了深入的变化。
在这里第一时间翻译成中文版。供你们学习分享之用。java
Java类库中包含许多必须经过调用close
方法手动关闭的资源。 好比InputStream
,OutputStream
和java.sql.Connection
。 客户常常忽视关闭资源,其性能结果可想而知。 尽管这些资源中有不少使用finalizer机制做为安全网,但finalizer机制却不能很好地工做(条目 8)。程序员
从以往来看,try-finally语句是保证资源正确关闭的最佳方式,即便是在程序抛出异常或返回的状况下:sql
// try-finally - No longer the best way to close resources! static String firstLineOfFile(String path) throws IOException { BufferedReader br = new BufferedReader(new FileReader(path)); try { return br.readLine(); } finally { br.close(); } }
这可能看起来并不坏,可是当添加第二个资源时,状况会变得更糟:编程
// try-finally is ugly when used with more than one resource! static void copy(String src, String dst) throws IOException { InputStream in = new FileInputStream(src); try { OutputStream out = new FileOutputStream(dst); try { byte[] buf = new byte[BUFFER_SIZE]; int n; while ((n = in.read(buf)) >= 0) out.write(buf, 0, n); } finally { out.close(); } } finally { in.close(); } }
这可能很难相信,但即便是优秀的程序员,大多数时候也会犯错误。首先,我在Java Puzzlers[Bloch05]的第88页上弄错了,多年来没有人注意到。事实上,2007年Java类库中使用close
方法的三分之二都是错误的。安全
即便是用try-finally语句关闭资源的正确代码,如前面两个代码示例所示,也有一个微妙的缺陷。 try-with-resources块和finally块中的代码均可以抛出异常。 例如,在firstLineOfFile
方法中,因为底层物理设备发生故障,对readLine
方法的调用可能会引起异常,而且因为相同的缘由,调用close
方法可能会失败。 在这种状况下,第二个异常彻底冲掉了第一个异常。 在异常堆栈跟踪中没有第一个异常的记录,这可能使实际系统中的调试很是复杂——一般这是你想要诊断问题的第一个异常。 虽然能够编写代码来抑制第二个异常,可是实际上没有人这样作,由于它太冗长了。性能
当Java 7引入了try-with-resources语句时,全部这些问题一会儿都获得了解决[JLS,14.20.3]。要使用这个构造,资源必须实现 AutoCloseable
接口,该接口由一个返回为void
的close
组成。Java类库和第三方类库中的许多类和接口如今都实现或继承了AutoCloseable
接口。若是你编写的类表示必须关闭的资源,那么这个类也应该实现AutoCloseable
接口。学习
如下是咱们的第一个使用try-with-resources的示例:翻译
// try-with-resources - the the best way to close resources! static String firstLineOfFile(String path) throws IOException { try (BufferedReader br = new BufferedReader( new FileReader(path))) { return br.readLine(); } }
如下是咱们的第二个使用try-with-resources的示例:调试
// try-with-resources on multiple resources - short and sweet static void copy(String src, String dst) throws IOException { try (InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst)) { byte[] buf = new byte[BUFFER_SIZE]; int n; while ((n = in.read(buf)) >= 0) out.write(buf, 0, n); } }
不只 try-with-resources版本比原始版本更精简,更好的可读性,并且它们提供了更好的诊断。 考虑firstLineOfFile
方法。 若是调用readLine
和(不可见)close
方法都抛出异常,则后一个异常将被抑制(suppressed),而不是前者。 事实上,为了保留你真正想看到的异常,可能会抑制多个异常。 这些抑制的异常没有呗被抛弃, 而是打印在堆栈跟踪中,并标注为被抑制了。 你也可使用getSuppressed
方法以编程方式访问它们,该方法在Java 7中已添加到的Throwable
中。code
能够在 try-with-resources语句中添加catch子句,就像在常规的try-finally语句中同样。这容许你处理异常,而不会在另外一层嵌套中污染代码。做为一个稍微有些作做的例子,这里有一个版本的firstLineOfFile
方法,它不会抛出异常,可是若是它不能打开或读取文件,则返回默认值:
// try-with-resources with a catch clause static String firstLineOfFile(String path, String defaultVal) { try (BufferedReader br = new BufferedReader( new FileReader(path))) { return br.readLine(); } catch (IOException e) { return defaultVal; } }
结论明确:在处理必须关闭的资源时,使用try-with-resources语句替代try-finally语句。 生成的代码更简洁,更清晰,而且生成的异常更有用。 try-with-resources语句在编写必须关闭资源的代码时会更容易,也不会出错,而使用try-finally语句其实是不可能的。