Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >为什么effective java 第三版推荐使用try-with-resources代替try-finally

为什么effective java 第三版推荐使用try-with-resources代替try-finally

作者头像
用户5397975
发布于 2020-11-05 10:36:05
发布于 2020-11-05 10:36:05
80200
代码可运行
举报
文章被收录于专栏:咖啡拿铁咖啡拿铁
运行总次数:0
代码可运行

背景

try-finally 这个语句想必做java的同学都不陌生吧,每当我们有关闭资源的需求我们都会使用到try-finally这个语句,比如我们在使用锁的时候,无论是本地的可重入锁还是分布式锁都会有下面类似的结构代码,我们会在finally里面进行unlock,用于强制解锁:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    Lock lock = new ReentrantLock();
    lock.lock();
    try{
        // doSometing
    }finally {
        lock.unlock();
    }

或者我们使用java的文件流读取或者写入文件的时候,我们也会在finally中强制关闭文件流,防止资源泄漏。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    InputStream inputStream = new FileInputStream("file");
    try {
        System.out.println(inputStream.read(new byte[4]));
    }finally {
        inputStream.close();
    }

其实乍一看 这样的写法应该没什么问题,但是如果我们出现了多个资源需要关闭我们应该怎么写呢?最常见的写法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
InputStream inputStream = new FileInputStream("file");
    OutputStream outStream = new FileOutputStream("file1");

    try {
        System.out.println(inputStream.read(new byte[4]));
        outStream.write(new byte[4]);
    }finally {
        inputStream.close();
        outStream.close();
    }

我们在外面定义了两个资源,然后在finally里面依次对这两个资源进行关闭,这个写法在我最开始写java的时候对文件流和数据库连接池做close的时候一些教学的文章都是这么教学的,那么这个哪里有问题呢?问题其实在于如果在inputStream.close的时候抛出异常,那么outStream.close()就不会执行,这很明显不是我们想要的结果,所以后面就改成了下面这种多重嵌套的方式去写:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    InputStream inputStream = new FileInputStream("file");
    try {
        System.out.println(inputStream.read(new byte[4]));
        try{
            OutputStream outStream = new FileOutputStream("file1");
            outStream.write(new byte[4]);
        }finally {
            outStream.close();
        }
    }finally {
        inputStream.close();
    }

在这种方式中即便是outStream.close()抛出了异常,但是我们依然会执行到inputStream.close(),因为他们是在不同的finally块,这个的确解决了我们的问题,但是还有两个问题没有解决:

  • 带来的第一个问题就是如果我们有不止两个资源,比如有十个资源,难道需要让我们写十个嵌套的语句吗?写完之后这个代码还能看吗?
  • 第二个问题就是如果我们在try里面出现异常,然后在finally里面又出现异常,就会导致异常覆盖,会导致finally里面的异常将try的异常覆盖了。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class CloseTest {

    public void close(){
        throw new RuntimeException("close");
    }

    public static void main(String[] args) {
        CloseTest closeTest = new CloseTest();
        try{
            throw new RuntimeException("doSomething");
        }finally {
            closeTest.close();
        }
    }

}
输出结果:Exception in thread "main" java.lang.RuntimeException: close

上面这个代码,我们期望的是能抛出doSomething的这个异常,但是实际的数据结果却是close的异常,这和我们的预期不符合。

try-with-resources

上面我们介绍了两个问题,于是在java7中引入了try-with-resources的语句,只要我们的资源实现了AutoCloseable这个接口那么我们就可以使用这个语句了,我们之前的文件流已经实现了这个接口那么我们可以直接使用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
try(InputStream inputStream = new FileInputStream("file");
            OutputStream outStream = new FileOutputStream("file1")) {
            System.out.println(inputStream.read(new byte[4]));
            outStream.write(new byte[4]);
        }

我们所有的资源定义全部都在try后面的括号中进行定义,通过这种方式我们就可以解决上面所说的几个问题:

  • 首先第一个问题,我们通过这样的方式,代码非常整洁,无论你有多少个资源,都可以很简洁的去做。
  • 第二个异常覆盖问题的话,我们可以通过实验来看一下,我们将代码改写为如下:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class CloseTest implements AutoCloseable {

    @Override
    public void close(){
        System.out.println("close");
        throw new RuntimeException("close");
    }

    public static void main(String[] args) {
        try(CloseTest closeTest = new CloseTest();
            CloseTest closeTest1 = new CloseTest();){
            throw new RuntimeException("Something");
        }
    }

}
输出结果为:
close
close
Exception in thread "main" java.lang.RuntimeException: Something
    at fudao.CloseTest.main(CloseTest.java:33)
    Suppressed: java.lang.RuntimeException: close
        at fudao.CloseTest.close(CloseTest.java:26)
        at fudao.CloseTest.main(CloseTest.java:34)
    Suppressed: java.lang.RuntimeException: close
        at fudao.CloseTest.close(CloseTest.java:26)
        at fudao.CloseTest.main(CloseTest.java:34)

我们在代码中定义了两个CloseTest,用来验证之前close出现异常是否会影响第二个,同时在close和try块里面都抛出不同的异常,可以看见我们的结果,输出了两个close,证明虽然close抛出异常,但是两个close都会执行。然后输出了doSomething的异常,可以发现这里我们输出的就是我们try块里面所抛出的异常,并且我们close的异常以Suppressed的方式记录在异常的堆栈里面,通过这样的方式我们两种异常都能记录下来。

try-with-resources原理

try-with-resources语句其实是一种语法糖,通过编译之后又回到了我们开始说的嵌套的那种模式:

可以发现try-with-resources被编译之后,又采取了嵌套的模式,但是和之前的嵌套有点不同,他close的时候都利用了catch去捕获了异常,然后添加到我们真正的异常中,整体逻辑比我们之前的嵌套要复杂一些。

总结

在我们关闭资源的时候,我们尽量优先推荐使用try-with-resources语句,但这里要注意的是很多资源其实是没有实现AutoCloseable接口的,比如我们最开始的Lock就没有实现这个接口,这个时候如果也想使用这种功能,可以采用组合的方式将Lock进行封装,来达到我们的目的。

如果大家觉得这篇文章对你有帮助,你的关注和转发是对我最大的支持,O(∩_∩)O:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 咖啡拿铁 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
你会使用try-with-resources吗
比如 InputStream、OutputStream,数据库连接对象 Connection,MyBatis中的 SqlSession 会话等。作为开发人员经常会忽略掉资源的关闭方法,导致内存泄漏。
cxuan
2019/09/11
1.4K0
你是否还在写try-catch-finally?来使用try-with-resources优雅地关闭流吧
开发中,我们常常需要在最后进行一些资源的关闭。比如读写文件流等,常见的,我们会在最后的finally里进行资源的关闭。但是这种写法是很不简洁的。其实,早在JDK1.7就已经引入了try-with-resources来关闭资源的方式,我们今天就来体验一下try-with-resources的简洁之处。
Happyjava
2019/07/16
1.4K0
你是否还在写try-catch-finally?来使用try-with-resources优雅地关闭流吧
try 语句如何更优雅的关闭资源?请看这里!
try-with-resources是 JDK 7 中引入的一个新的异常处理机制,它能让开发人员不用显式的释放try-catch语句块中使用的资源。
Java极客技术
2022/12/04
3980
今日代码 PK | 使用 try-with-resources 关闭资源
用于自动关闭实现了 AutoCloseable 或 Closeable 接口的资源,
程序员鱼皮
2024/03/25
1460
今日代码 PK | 使用 try-with-resources 关闭资源
Java7的try-with-resources声明(转)
看《Effective Java》第三版的时候,看到了其中建议将try-finally替换为try-with-resources。这个语法糖还算有意思,特此成文。
会说话的丶猫
2020/08/25
5570
Effective Java(一)
对于类而言,为了让客户端获取它自身的一个实例,最传统的方法就是提供一个公有的构造器。还有一种方法,也应该在每个程序员的工具箱中占有一席之地。类可以提供一个公有的静态工厂方法( static factory method ),它只是一个返回类的实例的静态方法。下面是一个来自 Boolean(基本类型 boolean 装箱类)的简单示例。这个方法将 boolean 基本类型值转换成了 Boolean 对象引用:
Remember_Ray
2020/08/03
6740
Java 虚拟机:JVM是如何处理异常的?
众所周知,异常处理的两大组成要素是抛出异常和捕获异常。这两大要素共同实现程序控制流的非正常转移。
码农架构
2021/02/07
1.7K0
Java 虚拟机:JVM是如何处理异常的?
java异常体系及1.7中的try-with-resources
异常指java运行过程出现的错误,在java中,将异常当作对象来处理,java.lang.Throwable是所有异常的超类。其架构如下图:
冬天里的懒猫
2020/08/03
7810
如何使用 try-with-resources 代替try-catch-finally?
try-with-resources 是 Java 7 引入的一种语法结构,用于自动关闭实现了 AutoCloseable 接口的资源。它可以代替传统的 try-catch-finally 结构来处理资源的释放。
程序员朱永胜
2023/10/10
2.2K0
Java 7新特性总结 - Coin项目新语言特性
OpenJDK中的Coin项目的目的是维护对Java语言所做的语法增强。 在Coin项目开始之初,曾经广泛地向社区征求提议。在短短的一个月时间内就收到了近70条提议。最后有9条提议被列入考虑之中。在这9条提议中,有6条成为Java 7的一部分,剩下的2条提议会在Java 8中重新考虑,还有1条提议被移到其他项目中实现。
干货满满张哈希
2021/04/12
6560
谈谈关于Exception 和 Error 的理解
世界上存在永远不会出现错误的程序吗?也许这只会出现在程序员的梦中。随着软件的诞生,异常就如影随形的围绕着我们,所以,只有正确处理好程序的意外情况,才能有效的避免这些异常。
cxuan
2019/06/03
7710
你还在使用 try-catch-finally 关闭资源?
作者:何甜甜在吗 链接:https://juejin.im/post/5b8f9fa05188255c6f1df755
Java技术栈
2020/07/03
8700
Exception和Error只知道用,不知道原理怎么行
在 Java 中的基本理念是 结构不佳的代码不能运行,发现错误的理想时期是在编译期间,因为你不用运行程序,只是凭借着对 Java 基本理念的理解就能发现问题。但是编译期并不能找出所有的问题,有一些 NullPointerException 和 ClassNotFoundException 在编译期找不到,这些异常是 RuntimeException 运行时异常,这些异常往往在运行时才能被发现。
淘课之家
2020/04/16
6200
Exception和Error只知道用,不知道原理怎么行
Java有哪些异常?
在 Java 中,所有的异常都有一个共同的祖先 java.lang 包中的 Throwable 类。Throwable 类有两个重要的子类 Exception(异常)和 Error(错误)。Exception 能被程序本身处理(try-catch), Error 是无法处理的(只能尽量避免)。Exception 和 Error 二者都是 Java 异常处理的重要子类,各自都包含大量子类。
黑洞代码
2021/01/14
1.9K0
Java有哪些异常?
看完这篇Exception 和 Error,和面试官扯皮就没问题了
在 Java 中的基本理念是 结构不佳的代码不能运行,发现错误的理想时期是在编译期间,因为你不用运行程序,只是凭借着对 Java 基本理念的理解就能发现问题。但是编译期并不能找出所有的问题,有一些 NullPointerException 和 ClassNotFoundException 在编译期找不到,这些异常是 RuntimeException 运行时异常,这些异常往往在运行时才能被发现。
cxuan
2020/04/14
4450
看完这篇Exception 和 Error,和面试官扯皮就没问题了
使用try-with-resources实现自动解锁
项目中使用Redission分布式锁,每次使用都需要显示的解锁。很麻烦,Java 提供了 try-with-resources 语法糖,它不仅可以用于自动关闭流资源,还可以用于实现自动解锁。
程序猿川子
2025/03/03
620
使用try-with-resources实现自动解锁
浅谈 Java 中的 AutoCloseable 接口
本文对 try-with-resources 语法进行了较为深入的剖析,验证了其为一种语法糖,同时给出了其实际的实现方式的反编译结果,相信你在看完本文后,关于 AutoCloseable 的使用你会有新的收获。
2020labs小助手
2020/11/02
2K0
java面试强基(11)
注意:不要在 finally 语句块中使用 return! 当 try 语句和 finally 语句中都有 return 语句时,try 语句块中的 return 语句会被忽略。这是因为 try 语句中的 return 返回值会先被暂存在一个本地变量中,当执行到 finally 语句中的 return 之后,这个本地变量的值就变为了 finally 语句中的 return 返回值。
一个风轻云淡
2023/10/15
1640
java面试强基(11)
你有没有掉进去过这些 Exception 的“陷阱“(Part C)
在test包中新增测试类ConcurrentModificationExceptionTest
RiemannHypothesis
2022/08/19
3050
你有没有掉进去过这些 Exception 的“陷阱“(Part C)
Java 中的 `try-catch-finally` 与 `try-with-resource`
https://blog.csdn.net/qq_29689343/article/details/95736854
訾博ZiBo
2025/01/06
1200
相关推荐
你会使用try-with-resources吗
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验