From 2a18fa4c6a983ab4f8a241621a7762f3e76df042 Mon Sep 17 00:00:00 2001 From: sjsdfg <736777445@qq.com> Date: Sat, 6 Oct 2018 09:33:31 +0800 Subject: [PATCH] =?UTF-8?q?9.=20=E4=BD=BF=E7=94=A8=20try-with-resources=20?= =?UTF-8?q?=E8=AF=AD=E5=8F=A5=E6=9B=BF=E4=BB=A3=20try-finally=20=E8=AF=AD?= =?UTF-8?q?=E5=8F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...try-with-resources语句替代try-finally语句.md | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 09. 使用try-with-resources语句替代try-finally语句.md diff --git a/09. 使用try-with-resources语句替代try-finally语句.md b/09. 使用try-with-resources语句替代try-finally语句.md new file mode 100644 index 0000000..77dc1ae --- /dev/null +++ b/09. 使用try-with-resources语句替代try-finally语句.md @@ -0,0 +1,92 @@ +# 9. 使用 try-with-resources 语句替代 try-finally 语句 + +--- +Java 类库中包含许多必须通过调用 `close` 方法手动关闭的资源。 比如 `InputStream`,`OutputStream` 和 `java.sql.Connection`。 客户经常忽视关闭资源,其性能结果可想而知。 尽管这些资源中有很多使用 finalizer 机制作为安全网,但 finalizer 机制却不能很好地工作(条目 8)。 + +从以往来看,try-finally 语句是保证资源正确关闭的最佳方式,即使是在程序抛出异常或返回的情况下: + +```Java +// 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(); + } +} +``` + +这可能看起来并不坏,但是当添加第二个资源时,情况会变得更糟: + +```Java +// 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 的示例: + +```Java +// 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 的示例: + +```Java +// 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` 中。 + +可以在 try-with-resources 语句中添加 catch 子句,就像在常规的 try-finally 语句中一样。这允许你处理异常,而不会在另一层嵌套中污染代码。作为一个稍微有些做作的例子,这里有一个版本的 `firstLineOfFile` 方法,它不会抛出异常,但是如果它不能打开或读取文件,则返回默认值: + + +```Java +// 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 语句实际上是不可能的。 + +