【第9条】try-with-resources优先于try-finally

try-with-resources优先于try-finally

 

Java 类库中包含许多必须通过调用 close 方法手动关闭的资源。例如 InputStream , OutputStream 和 java.sql.Connection 。客户经常会忽略资源的关闭,造成严重的性能后果也就可想而知了。虽然这其中的许多资源都是用终结方法作为安全网,但是效果并不理想(详见第 8 条)。

 

根据经验,try-finally语句是确保资源会被适时关闭的最佳方法,就算发生异常或者返回也一样

// 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();   } }

这可能令人有点难以置信,不过就算优秀的程序员也会经常犯这样的错误。事实上,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 方法还可以访到它们,getSuppressed 方法也已经添加在Java7的  Throwable中了。

 

在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是不可能做到的。

 

                                                                                             关注公众号

                                                                                            每天干货分享

                                                          

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值