#华为云·寻找黑马程序员#【代码重构之路】如何优雅的关闭外部资源

1、背景

在Java编程中,如果打开了外部资源(文件、数据库连接、网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们。因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制,如果我们不在编程时确保在正确的时机关闭外部资源,就会导致外部资源泄露,紧接着就会出现文件被异常占用,数据库连接过多导致连接池溢出等诸多很严重的问题

2、传统的资源关闭

为了确保外部资源一定要被关闭,通常关闭代码被写入finally代码块中,当然我们还必须注意到关闭资源时可能抛出的异常,于是变有了下面的经典代码

FileInputStream fis = null;
try {
	fis = new FileInputStream(new File("test"));
    System.out.println(fis.read());
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (fis != null) {
    	try {
        	fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
     }
}
  • 关闭外部资源时,每次都要判空,调用close时还要继续捕获异常
  • 当try中打开了多个外部资源时,需要逐个关闭,finally代码臃肿

3、重构

3.1 try-with-resources

在JDK1.7及其之后,可以使用try-with-resources语法,来重构上面的代码,使其更加简洁
那什么是try-with-resources呢?简而言之,当一个外部资源的句柄对象(比如FileInputStream对象)实现了AutoCloseable或者Closeable接口,那么就可以将上面的样式代码简化为如下形式:

try (FileInputStream fis = new FileInputStream(new File("test"))) {
	System.out.println(fis.read());
} catch (IOException e) {
    e.printStackTrace();
}
  • 被自动关闭的资源需要实现Closeable或者AutoCloseable接口
  • 将要关闭的外部资源在try()中创建,catch()捕获处理异常
  • 自动调用资源的close方法

将外部资源的句柄对象的创建放在try关键字后面的括号中,当这个try-catch代码块执行完毕后,Java会确保外部资源的close方法被调用,代码瞬间简洁了很多

3.2 原理

try-with-resources机制是一种语法糖,其底层实现原理仍然是try-catch-finally写法,我们可以来看下上面那段代码反编译后的效果:

try {
	FileInputStream fis = new FileInputStream(new File("test"));
    Throwable var2 = null;
    try {
    	System.out.println(fis.read());
    } catch (Throwable var12) {
    	var2 = var12;
        throw var12;
    } finally {
        if (fis != null) {
        	if (var2 != null) {
                try {
                    fis.close();
                } catch (Throwable var11) {
                    var2.addSuppressed(var11);
                }
             } else {
                    fis.close();
             }
         }
     }
} catch (IOException var14) {
    var14.printStackTrace();
}       

大家可能注意到代码中有一处对异常的特殊处理

var2.addSuppressed(var11);

这是try-with-resources语法涉及的另外一个知识点,叫做异常抑制。当对外部资源进行处理时,如果遭遇了异常,且在随后的关闭外部资源过程中,又遭遇了异常,那么你catch到的将会是对外部资源进行处理时遭遇的异常,关闭资源时遭遇的异常将被“抑制”但不是丢弃,通过异常的getSuppressed方法,可以提取出被抑制的异常

3.3 try-with-resources 也支持声明多个资源
try (FileInputStream fin = new FileInputStream(new File("input.txt"));
	FileOutputStream fout = new FileOutputStream(new File("out.txt"));
    GZIPOutputStream out = new GZIPOutputStream(fout)) {
    byte[] buffer = new byte[4096];
    int read;
    while ((read = fin.read(buffer)) != -1) {
    	out.write(buffer, 0, read);
    }
} catch (IOException e) {
	e.printStackTrace();
}
  • 资源关闭会按声明时的相反顺序被执行
中文版(共6个文件)  本书荣获第15届生产效率大奖!《设计模式》和《重构》之后又一里程碑式著作,凝聚众多业界专家经验与领悟,帮你打通重构与模式任督二脉。   1994年,《设计模式》为我们带来了常见设计问题的经典解决方案,从而改变了整个面向对象开发的面貌。   1999年,《重构》为我们带来了一种改进代码的高效过程,从而彻底改变了面向对象设计的方式。   现在,在众所期盼之中,Joshua Kerievsky的《重构与模式》第一次深入和全面地论述了设计模式的运用与演进式的重构过程,并揭示了两者之间至关重要的联系,又一次革命性地改变了我们的设计方式。   本书阐述了模式导向重构的理论与实践,提出使用模式改善既有设计优于在设计早期使用模式的重要观点。书中包含了27种模式导向重构,每种重构通过一系列较小的重构,安全地在设计中实现、趋向和去除模式。实例均出自真实项目的代码。通过本书,读者不仅能够更好地学习和理解模式和重构,了解两个领域的新进展和业界最新领悟,还能够学到实战中实现模式的多种方式,学会如何在不同情况下进行选择。   设计模式和重构对我们来说早已不是什么陌生的字眼了。1994年,GoF的巨著《设计模式》初次向世人展示了设计模式的魅力。2002年,Martin Fowler的《重构:改善既有代码的设计》则刮起了一阵重构的旋风。记得在《重构》刚刚出版的时候,软件开发界和评论界就赞扬它是一本具有与《设计模式》同等高度的图书。我相信本书的每一位读者都和我一样,早已收藏了这两本书,反复阅读,仔细品味,并从中获益匪浅。   Ralph Johnson序一《设计模式》一书中叙述了使用模式的几种方式。有些人在编写任何代码之前,都要很早地为模式做计划,而有些人在编写了大量代码之后才开始添加模式。第二种使用模式的方式就是重构,因为是要在不增加系统特性或者不改变其外部行为的情况下改变系统的设计。有些人在程序中加入模式,只是因为觉得模式能够使程序更容易修改;更多人这样做只是为了简化目前的设计。如果代码已经编写,这两种情形都是重构,因为前者是通过重构使修改更容易,而后者则是通过重构在修改后进行整理。虽然模式是在程序中能够看到的东西,但是模式也是一种程序转换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值