try-with-resources与try-finally优缺点比较

1、try-with-resources的用法

jdk1.7引入了一个语法叫 try-with-resource语法,它的使用如下:

如果一个类实现了 AutoCloseable 接口,并行重写 close 方法。那么这个类就可以写在try-catch的try后面的括号中,并且能在try-catch块执行后自动执行这个方法。
举例如下:

public class TryWithResource implements AutoCloseable {
 
    private int age = 18;
 
    @Override
    public void close() throws Exception {
        System.out.println("this is close 方法");
    }
 
    public static void main(String[] args) {
        try (TryWithResource tryWithResource = new TryWithResource()) {
            System.out.println(tryWithResource.age);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行这个方法,我们看到打印结果:

18
this is close 方法

很明显这样写和下面这种经典写法的效果是一样的,只是看上去能更加简便一些。

public class TryWithResource{
 
    private int age = 18;
 
    public void close() throws Exception {
        System.out.println("this is close 方法");
    }
 
    public static void main(String[] args) {
        TryWithResource tryWithResource = new TryWithResource();
        try {
            System.out.println(tryWithResource.age);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                tryWithResource.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

这个经典的写法,是不是很熟悉,java编程中很多对于资源的操作,比如文件流等需要连接的资源,都需要在finally中手动关闭资源,但又怕关闭资源抛异常,所以最终就是finally块中又加了try-catch块,这个结构确实怎么看怎么不得劲,但没办法,JDK1.7之前只能只样写。
但是在JDK1.7后,就可以通过接口的方式,优雅的写这些代码了。

举个例子:

默认的java.io.FileReader等类,已经实现了这个接口了,所以我们在操作文件时,就可以直接用这种优雅的方式来写了,


2、try-with-resources的优势

在java开发中,一些网络链接或者是文件资源都需要程序员去手动调用close方法关闭,比如InputStream、OutputStream和java.sql.Connection。如果忘关了就可能造成严重的性能后果。而关闭的方法有很多种。比如finalizer、try-catch-finally、try-with-resources等等。

finalizer机制可以关闭,但是其执行性不可预测,还有可能造成内存泄漏,所以一般不使用,虽然java9还提出了cleaner机制代替了finalizer机制,但是其执行依然不可预测,因此选择就落在了try-catch-finally和try-with-resources之间。

本文就是为了讨论该选择哪一种比较好,不过题目已经给出了答案肯定是try-with-resources。下面带着这个答案去分析为什么推荐使用try-with-resources而不是try-finally。

一、前言

在正式分析之前,我们先看一波finally的执行顺序。

1、finally不是必要条件

也就是说try-catch-finally中,可以只有try-catch,也可以只有try-finally。

2、假设基于try-catch-finally:

第一:代码没有异常

执行顺序:try执行完整->catch不执行->finally执行

第二:代码有异常且catch进行捕获

执行顺序:try执行部分->跳转catch捕获处理->finally执行

第三:代码有异常且catch不捕获:这种情况没有catch

执行顺序:try执行部分->finally执行

从上面的执行顺序可以看出,finally语句不管在哪种情况是一定会执行的。基于这个认识,现在我们再来分析。

二、try-finally的缺点
static String firstLineOfFile(String path) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader(path));
        try {
            return reader.readLine();
        } finally {
            reader.close();
        }
}

关闭一个资源还好,但是如果再添加第二个资源,代码看起来就会一团糟了。

static void copy(String src, String desc) throws IOException {
        InputStream in = new FileInputStream(src);
        try {
            OutputStream out = new FileOutputStream(desc);
            byte[] bytes = new byte[1024];
            int n;
            try {
                while ((n = in.read(bytes)) != -1) {
                    out.write(bytes, 0, n);
                }
            } finally {
                out.close();
            }
        } finally {
            in.close();
        }
}

如果需要关闭的资源不仅种类多,而且数量也很多。那代码可就太庞大了。现在对这种方式的缺点进行一波总结:

  1. 关闭的资源多事,代码复杂
  2. 对于第一个案例,如果设备出现异常,那么那么调用readLine就会抛出异常,同时close方法也出现异常,在这种情况下,close异常会完全抹去readLine异常。在异常堆栈轨迹中也完全没有readLine异常的记录。

现在来测试一边:

基于以上原因,出现了try-with-resources。

三、try-with-resources的优势 

try-with-resources是在jdk1.7引入的,可以完美解决以上的问题。要使用这个构造的资源,必须先实现AutoCloseable接口,其中包含了单个返回void的close方法,Java类库与第三方类库中的许多类和接口,现在都实现或扩展了AutoCloseable接口,因此我们现在不必实现了。

既然try-with-resources能够解决以上的问题,现在来看一下,如何解决的:

1、代码复杂问题解决

static void copy(String src, String desc) throws IOException {
        try (InputStream in = new FileInputStream(src);
             OutputStream out = new FileOutputStream(desc)) {
            byte[] bytes = new byte[1024];
            int n;
            while ((n = in.read(bytes)) != -1) {
                out.write(bytes, 0, n);
            }
        }
}

可以看出这种方式代码更加简单,出现了错误,也能快速定位。

2、异常抹去问题解决

static String firstLineOfFil  (String path) throws IOException {
        try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
            return reader.readLine();
        }
}

如果readLine和不可见的close方法都抛出异常,close方法抛出的异常就会被禁止,try-finally处理机制中我们无法看到,堆栈轨迹中也不能打印,但是try-with-resources不一样,全部会被打印在堆栈轨迹中,并注明它们是被禁止的异常,通过编写调用getSuppressed方法还可以访问到它们。
现在再来测试一遍。

OK,上面基本上全部分析完毕,但是此书还给出了一个更好的案例: 

static String firstLineOfFile(String path, String defaultVal) {
        try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
            return reader.readLine();
        } catch (IOException e) {
            return defaultVal;
        }
}

 这个firstLineOfFile方法没有抛出异常,但是如果它无法打开文件,或者无法从中读取,就会返回一个默认值。

结论
处理必须关闭的资源时,始终要优先考虑使用try-with-resources,而不是try-finally。这样得到的代码将更简洁,清晰,产生的异常也更有价值,这些也是try-finally无法做到的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值