Java中请优先使用try-with-resources而非try-finally

Java库包含了很多需要手工调用close方法来关闭的资源。比如说InputStream、OutputStream及java.sql.Connection。关闭资源常常会被客户端所忽视,这会导致可怕的性能问题。虽然很多资源使用了终结器来作为安全网,不过终结器却并不那么尽如人意。

从历史上来看,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();
}
}

虽然难以相信,但即便是优秀的程序员很多时候也会编写这样的代码。对于初学者来说,我在Java Puzzlers [Bloch05]的第88页指出了问题,但多年来没人注意到。事实上,2007年,在Java库中对close方法的使用有2/3是错误的。

甚至使用try-finally语句关闭资源的正确代码(如前面两个代码示例所示)也存在一些小问题。try块与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方法都抛出了异常,那么后者的异常就会被前者压制住。事实上,多个异常都会被压制住,从而保留你实际想看的那个异常。这些被压制的异常并不是被丢弃掉;他们会在堆栈中打印出来,并且有相应的符号表示他们被压制了。你还可以通过编程的方式通过getSuppressed方法来访问这些异常,该方法是在Java 7中被添加到Throwable中的。

你可以将catch从句放到try-with-resources语句上,就像常规的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所做不到的。

Java中请优先使用try-with-resources而非try-finally的更多相关文章

  1. java中请给一个Abstract类实现接口的实例!

    2.Abstract类实现接口 马克-to-win:如果实现某接口的类是abstract类,则它可以不实现该接口所有的方法.但其非abstract的子类中必须拥有所有抽象方法的实在的方法体:(当然它a ...

  2. java中请给出一个return this的例子。

    [新手可忽略不影响继续学习]下面例子中setYear中的return this;返回了一个指向对象的指针,this.setMonth(8).setDay(20);是合法的,如果像原来的例子一样什么都不 ...

  3. java中请给出一个抽象类,可以继承实体类的例子

    例1.7.2(抽象类可以继承实体类)- class VehMark_to_win {    void steer() {        System.out.println("Turn st ...

  4. java中请给出例子程序:找出两个数的最大公约数和最小公倍数

    9.2 找出12和8的最大公约数和最小公倍数.     public class Test {     public static void main(String[] args) {         ...

  5. java中请给出例子程序:找出n到m之间的质数。

    9.1 找出100到200之间的质数.  public class Test {     public static void main(String[] args){         for (in ...

  6. 关于java中为什么尽量把受检异常转化为非受检异常

    首先理解一下受检异常与非受检异常: 异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机操作中可能遇到的异常,是一种常见的运行错误,只要程序设计的没有问题通常就不会发生.受检异常与程序的上 ...

  7. Java中静态变量、静态代码块、非静态代码块以及静态方法的加载顺序

    在研究单例设计模式的时候,用到了静态变量和静态方法的内容,出于兴趣,这里简单了解一下这四个模块在类初始化的时候的加载顺序. 经过研究发现,它们的加载顺序为: 1.非静态代码块 2.静态变量或者静态代码 ...

  8. Java中的资源文件加载方式

    文件加载方式有两种: 使用文件系统自带的路径机制,一个应用程序只能有一个当前目录,但可以有Path变量来访问多个目录 使用ClassPath路径机制,类路径跟Path全局变量一样也是有多个值 在Jav ...

  9. c++中继承和java中继承的对比

    java中: class Parent{ public void test(int a){ System.out.println("Parent:" + a); System.ou ...

随机推荐

  1. iPhoneSE2要在印度独家生产真得没戏?

    现在,关于iPhone SE2的消息层出不穷,总的来说,它是一款真实存在的手机,整体性能和iPhone5X/SE相似,大概可能差不多会加上一些"无线充电"之类的无聊功能.普通消费者 ...

  2. unittest(22)- p2p项目实战(7)-do_mysql

    # 7. do_msql.py import mysql.connector from p2p_project_2020_1_21.tools import project_path from p2p ...

  3. 关于sql拼接的知识点 where1=1

    String sql="select * from tab_route where 1 = 1 "; 这样不会报错,而且可以根据情况,再去拼接sql 可以使用if(){}else{ ...

  4. java文件压缩ZipOutPutStream

    其实最好的书籍就是javaAPI 1.创建ZipOutPutStream流,利用BufferedOutputStream提个速. 2.新建zip方法,用来压缩文件,传参 3.zip方法利用putNex ...

  5. Selenium自动化测试实例-基于python

    一.Selenium介绍 Selenium是一个Web开源自动化测试框架,具有页面级操作.模拟用户真实操作.API从系统层面触发事件等特点. 1.版本 Selenium 1.0  Sever/Clie ...

  6. QA、EPG、PMO各自的职能划分及关系是什么?

    团队 职能 主要工作内容 EPG 负责过程持续改进工作 公司规范的建设和推广,并持续改进.收集过程改进需求,制定过程改进计划,获得高层的支持,并实施改进工作. PMO 负责公司内所有项目的审核.管理 ...

  7. javaScript系列 [28]

    本文介绍JavaScript事件相关的知识点,主要包括事件流.事件处理程序.事件对象(event)以及常见事件类型和事件委托等相关内容. 在网页开发涉及的三种基础技术(HTML CSS JavaScr ...

  8. 在博客中显示图片_Mac版

    主要是防止自己忘掉 为了解决一开始自己想在写入的博客中添加本地图片,直接链接的话在自己的电脑倒是可以显示图片,但是在别人的电脑上就没办法加载图片了,问各路大神也没人愿意解答,百度也没有想要的答案,只好 ...

  9. JasperReport框架使用教程(附带常见空白页问题说明)

    概述与安装使用 1. PDF报表概述 概述 ​ 在企业级应用开发中,报表生成.报表打印下载是其重要的一个环节.在之前的课程中我们已经学习了报表中比较重要的一种:Excel报表.其实除了Excel报表之 ...

  10. R语言入门级实例——用igragh包分析社群

    R语言入门级实例——用igragh包分析社群 引入—— 本文的主要目的是初步实现R的igraph包的基础功能,包括绘制关系网络图(social relationship).利用算法进行社群发现(com ...