为什么推荐使用try-with-resources代替try-finally

简介: 这篇文章是我近期看了《Effective java》一书中总结的,来自其中第九条。为了对其理解的更加透彻,因此重新分析了一下,并加入了一些其他点。本文的所有例子均在本地代码运行完毕基于JDK版本1.8,运行环境eclipse本文类名:TryWithResources,下文的堆栈信息也以此为基础在java开发中,一些网络链接或者是文件资源都需要程序员去手动调用close方法关闭,比如InputStream、OutputStream和java.sql.Connection。如果忘关了就可能造成严重的性能后果。而关闭的方法有很多种。比如finalizer、try-catch-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的缺点


先看案例,本案例来自《Effective java》,现在要关闭资源:

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异常的记录。


现在来测试一边:

v2-47b36a0f0751133457aa244e12a0dade_1440w.jpg

基于以上原因,出现了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方法还可以访问到它们。 现在再来测试一遍。

v2-18cfe131bd3277609c7dc9db94a93545_1440w.jpg

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无法做到的。

相关文章
|
7月前
|
Java 测试技术
使用try-catch-finally关闭资源更优雅的关闭try-with-resource
使用try-catch-finally关闭资源更优雅的关闭try-with-resource
113 1
try catch finally,try 里面有 return,finally 还执行吗?
try catch finally,try 里面有 return,finally 还执行吗?
79 0
|
Java 编译器 数据库连接
如何使用 try-with-resources 代替try-catch-finally?
如何使用 try-with-resources 代替try-catch-finally?
|
监控 前端开发 Java
Caused by: org.yaml.snakeyaml.scanner.ScannerException: while scanning an alias in ‘reader‘, line 5
Caused by: org.yaml.snakeyaml.scanner.ScannerException: while scanning an alias in ‘reader‘, line 5
168 0
|
XML Java 数据库连接
【Error】:BeanCreationException: Error creating bean(Could not resolve resource location)
【Error】:BeanCreationException: Error creating bean(Could not resolve resource location)
380 0
【Error】:BeanCreationException: Error creating bean(Could not resolve resource location)
Exception in thread “main“ java.io.IOException: Could not find resource mybatis-config.xml
Exception in thread “main“ java.io.IOException: Could not find resource mybatis-config.xml
Exception in thread “main“ java.io.IOException: Could not find resource mybatis-config.xml
|
Java
【学习笔记】【Java】try-catch-finally中,finally是在什么时候执行的:try结束、catch结束、return前
结论:try-catch-finally中,finally执行:try结束、catch结束、return前
195 0
【学习笔记】【Java】try-catch-finally中,finally是在什么时候执行的:try结束、catch结束、return前
try-catch-finally 和 return 是怎么执行的?
来源:http://liangfei.me/ 最近一直在看Java虚拟机规范,发现直接分析bytecode更能加深对Java语言的理解。 之前看过一篇关于 return 和 finally 执行顺序的文章,仅在 Java 的语言层面做了分析,其实我倒觉得直接看 bytecode 可能来的更清晰一点。
|
Android开发
打包时Resource is out of sync with the file system 报错问题
打包时Resource is out of sync with the file system 报错问题
172 0
|
Docker 容器 .NET
System.Drawing.Common在docker报错 The type initializer for 'Gdip' threw an exception
今天在asp.net core站点上做一个发送邮件附带二维码的功能,为了方便邮件接受者直接手机扫描打开特定h5页面。采用QRCoder,代码很简单几行 QRCodeGenerator qrGenerator = new QRCodeGenerator(); QRCodeData qrCodeData = qrGenerator.
4550 0