前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >为什么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
80300
代码可运行
举报
文章被收录于专栏:咖啡拿铁咖啡拿铁
运行总次数: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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
C#创建安全的字典(Dictionary)存储结构
彭泽0902
2018/01/04
2.5K0
浅析C# Dictionary实现原理
本篇文章配图以及文字其实整理出来很久了,但是由于各种各样的原因推迟到现在才发出来,还有之前立Flag的《多线程编程》的笔记也都已经写好了,只是说还比较糙,需要找个时间整理一下才能和大家见面。
全球技术精选
2021/08/20
2370
Dictionary源码解析及实现原理(C#)
Dictionary 又称C#中的哈希表,是一个Collection(集合)类型,可以通过Key/Value(键值对)的形式来存放数据;该类最大的优点就是它查找元素的时间复杂度接近O(1),实际项目中常被用来做一些数据的本地缓存,提升整体效率。
用户11236293
2024/08/14
1450
.net源码分析 – Dictionary<TKey, TValue>
接上篇:.net源码分析 – List<T> Dictionary<TKey, TValue>源码地址:https://github.com/dotnet/corefx/blob/master/src
用户1147588
2018/01/04
1.8K0
.net源码分析 – Dictionary<TKey, TValue>
算法和数据结构: 十一 哈希表
在前面的系列文章中,依次介绍了基于无序列表的顺序查找,基于有序数组的二分查找,平衡查找树,以及红黑树,下图是他们在平均以及最差情况下的时间复杂度:
yaphetsfang
2020/07/30
1K0
算法和数据结构: 十一 哈希表
你真的了解字典吗(dictionary)?
半年前,我参加我现在所在公司的面试,面试官给了一道题,说有一个Y形的链表,知道起始节点,找出交叉节点.
码农阿宇
2019/02/13
6810
DotNet Dictionary 实现简介
本来笔者对DotNet的Hashtable及Dictionary认识一直集中在使用上,一个直接用object 一个可以用泛型,以前也只大概看过Hashtable的实现。最近查MSDN时发现有建议开发者使用Dictionary代替Hashtable的描述,出于好奇测试了Hashtable及Dictionary读写性能,发现无论读还是写Dictionary都大幅领先Hashtable,然后就花时间整理了Dictionary操作逻辑试图找到这种性能提升的原因(最后会发现实现上的差异带来的性能明显提升也算的上是理所当然)。下文实际是介绍的Dictionary的实现(调试中使用的源是corefx 3.1),其中穿插着对比了Hashtable的实现逻辑。
lulianqi
2022/03/11
3560
DotNet Dictionary 实现简介
.net源码分析 - ConcurrentDictionary<TKey, TValue>
List源码分析 Dictionary源码分析 ConcurrentDictionary源码分析 继上篇Dictionary源码分析,上篇讲过的在这里不会再重复 ConcurrentDiction
用户1147588
2018/01/04
1.3K0
ConsurrentDictionary并发字典知多少?
背景 在上一篇文章你真的了解字典吗?一文中我介绍了Hash Function和字典的工作的基本原理. 有网友在文章底部评论,说我的Remove和Add方法没有考虑线程安全问题. https://d
码农阿宇
2019/03/20
8930
ConsurrentDictionary并发字典知多少?
C#集合类型大揭秘
集合是.NET FCL(Framework Class Library)的重要组成部分,我们平常撸C#代码时免不了和集合打交道,FCL提供了丰富易用的集合类型,给我们撸码提供了极大的便利。正是因为这种与生俱来的便利性,使得我们对集合既熟悉又陌生。很多同学可能一直还是停留在使用的层面上,那么今天我们一起来深入学习一下C#语言中的各种集合。
撸码那些事
2018/06/15
1.2K0
C#集合类型大揭秘
我大意了,没有闪。
(1) 栗子1重新new赋值难道不是修改了原字典对象newDict吗?foreach字典为什么不报InvalidOperation异常?
有态度的马甲
2023/09/05
2520
我大意了,没有闪。
【算法与数据结构】--高级算法和数据结构--哈希表和集合
哈希表(Hash Table)是一种常用的数据结构,其核心原理是将数据存储在数组中,并使用哈希函数来映射数据的键(Key)到数组中的特定位置,这个位置通常被称为“哈希桶”或“槽位”。哈希表允许快速的数据查找、插入和删除操作,通常在平均情况下,这些操作的时间复杂度为O(1)。以下是哈希表的基本原理:
喵叔
2023/10/17
5380
C# Dictionary 字典
说明 必须包含名空间System.Collection.Generic Dictionary里面的每一个元素都是一个键值对(由二个元素组成:键和值) 键必须是唯一的,而值不需要唯一的 键和值都可以是任何类型(比如:string, int, 自定义类型,等等) 通过一个键读取一个值的时间是接近O(1) 键值对之间的偏序可以不定义
zls365
2021/03/16
1.2K0
.NET中的泛型集合
近对集合相关的命名空间比较感兴趣,以前也就用下List, Dictionary<Tkey, TValue>之类,总之,比较小白。点开N多博客,MSDN,StackOverflow,没找到令我完全满意的答案,本打算自己总结下写出来,工作量好大的感觉……直到昨晚随意翻到看了一些又放下的《深入理解C#》-附录B部分,高兴地简直要叫出来——“这总结真是太绝了,好书不愧是好书”。真是“踏破铁鞋无觅处,得来全不费工夫”,最好的资源就在眼下,而自己居然浑然不知。或许只有深入技术细节的时候,才能认识到经典为什么经典吧!言归正传,本博客主要是对《深入理解C#》-附录B的摘录,并加了些标注。
郑子铭
2023/08/29
2850
.NET中的泛型集合
数据结构基础温故-6.查找(下):哈希表
哈希(散列)技术既是一种存储方法,也是一种查找方法。然而它与线性表、树、图等结构不同的是,前面几种结构,数据元素之间都存在某种逻辑关系,可以用连线图示表示出来,而哈希技术的记录之间不存在什么逻辑关系,它只与关键字有关联。因此,哈希主要是面向查找的存储结构。哈希技术最适合的求解问题是查找与给定值相等的记录。
Edison Zhou
2018/08/20
6270
数据结构基础温故-6.查找(下):哈希表
C#中HashTable、Dictionary、ConcurrentDictionary区别
HashTable表示键/值对的集合。在.NET Framework中,Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似key-value的键值对,其中key通常可用来快速查找,同时key是区分大小写;value用于存储对应于key的值。Hashtable中key-value键值对均为object类型,所以Hashtable可以支持任何类型的keyvalue键值对,任何非 null 对象都可以用作键或值。
用户9127601
2021/11/01
8630
.NET 排序 Array.Sort<T> 实现分析
System.Array.Sort<T> 是.NET内置的排序方法, 灵活且高效, 大家都学过一些排序算法,比如冒泡排序,插入排序,堆排序等,不过你知道这个方法背后使用了什么排序算法吗? 先说结果,
全球技术精选
2021/10/09
6590
.NET 排序 Array.Sort<T> 实现分析
CSharp中字典(Dictionary)的使用
Dictionary<TKey, TValue> 是泛型类型,其中 TKey 表示键的类型,TValue 表示值的类型。
码客说
2024/05/30
4090
C#集合类型大揭秘
集合是.NET FCL(Framework Class Library)的重要组成部分,我们平常撸C#代码时免不了和集合打交道,FCL提供了丰富易用的集合类型,给我们撸码提供了极大的便利。正是因为这种
撸码那些事
2018/06/21
1.6K0
面试时被问到Flutter/Dart的HashMap怎么办?
相信不少同学在面试的时候有被问到关于HashMap的问题,特别是Java/Android程序员,HashMap几乎是必然会被提及的。因为这里面可以挖掘的点实在是太多了。关于Java的HashMap面经在网上可以说是随处可见了。自然而然,随着Flutter的火爆,后面大家也可能在面试中被问到关于Flutter/Dart的HashMap相关问题。与其到时候一问三不知,不如现在就来了解一下Flutter/Dart的HashMap吧。
HowHardCanItBe
2021/05/21
1.2K0
相关推荐
C#创建安全的字典(Dictionary)存储结构
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档