Java语法糖 : 使用 try-with-resources 语句安全地释放资源

简介: 使用 try-with-resources 语句自动关闭资源的类都实现了AutoCloseable 接口。

先给出本文的重点:

  1. 这里所谓的资源(resource)是指在程序完成后,必须关闭的对象, try-with-resources 语句确保了每个资源在语句结束时关闭;
  2. 使用 Java 7 新增的 try-with-resources 语句 代替 try-finally 语句进行资源关闭,不仅代码更精简而且更安全;
  3. 支持 try-with-resources 语句 的类必须都实现 AutoCloseable接口,同样的,我们自定义的类也可以实现这个接口来帮助我们进行一些安全的自动化释放资源;
  4. Java 9 对 try-with-resources 语句进行了改进,如果你有一个资源是 final 或等效于 final 变量, 则可以在 try-with-resources 语句中使用该变量,无需在 try-with-resources 语句中再声明一个新的变量。

下面就通过几个简单而实用的例子,给大家演示一下 try-with-resources 语句的各种用法。


Java 7 之前的 try-finally 语句

之前操作资源,为了防止因为异常造成无法关闭资源,都是通过 try-finally 语句来关闭资源流的。


这样做有两个弊端:

  1. 代码丑陋
  2. 不安全

例如下面读写文件的一个方法,需要定义大量的变量,以及反复的异常捕捉和close操作。

publicstaticvoidmethod1() {
FileWriterfileWriter=null;
BufferedWriterbufferedWriter=null;
FileReaderfileReader=null;
BufferedReaderbufferedReader=null;
Filefile=newFile("try-with-resources-demo.txt");
try {
fileWriter=newFileWriter(file);
bufferedWriter=newBufferedWriter(fileWriter);
fileReader=newFileReader(file);
bufferedReader=newBufferedReader(fileReader);
bufferedWriter.write("now is:"+LocalDateTime.now() +"\n\r");
bufferedWriter.write("availableProcessors are : "+Runtime.getRuntime().availableProcessors() +"\n\r");
bufferedWriter.write("totalMemory is : "+Runtime.getRuntime().totalMemory() +"\n\r");
bufferedWriter.write("maxMemory is : "+Runtime.getRuntime().maxMemory() +"\n\r");
bufferedWriter.write("freeMemory is : "+Runtime.getRuntime().freeMemory() +"\n\r");
bufferedWriter.flush();
StringBufferreadResult=newStringBuffer("");
StringoneLine=null;
while (null!= (oneLine=bufferedReader.readLine())) {
readResult.append(oneLine+"\n\r");
        }
System.out.println(readResult.toString());
    } catch (IOExceptionioe) {
//TODO log: IOExceptionioe.printStackTrace();
    } finally {
try {
if (null!=fileReader)
fileReader.close();
        } catch (IOExceptionioe) {
//TODO log: close stream has an  IOExceptionioe.printStackTrace();
        }
try {
if (null!=bufferedReader)
bufferedReader.close();
        } catch (IOExceptionioe) {
//TODO log: close stream has an  IOExceptionioe.printStackTrace();
        }
try {
if (null!=fileWriter)
fileWriter.close();
        } catch (IOExceptionioe) {
//TODO log: close stream has an  IOExceptionioe.printStackTrace();
        }
try {
if (null!=bufferedWriter)
bufferedWriter.close();
        } catch (IOExceptionioe) {
//TODO log: close stream has an  IOExceptionioe.printStackTrace();
        }
    }
}

这样的程序,显然不是有代码洁癖的小伙伴可以接受的。


try-with-resources 语句

而使用 try-with-resources 语句实现的话,就简洁了很多。

public static void method2() {
        File file = new File("try-with-resources-demo.txt");
        try (
                FileWriter fileWriter = new FileWriter(file);
                BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
                FileReader fileReader = new FileReader(file);
                BufferedReader bufferedReader = new BufferedReader(fileReader);
        ) {
            bufferedWriter.write("now is:" + LocalDateTime.now() + "\n\r");
            bufferedWriter.write("availableProcessors are : " + Runtime.getRuntime().availableProcessors() + "\n\r");
            bufferedWriter.write("totalMemory is : " + Runtime.getRuntime().totalMemory() + "\n\r");
            bufferedWriter.write("maxMemory is : " + Runtime.getRuntime().maxMemory() + "\n\r");
            bufferedWriter.write("freeMemory is : " + Runtime.getRuntime().freeMemory() + "\n\r");
            bufferedWriter.flush();
            StringBuffer readResult = new StringBuffer("");
            String oneLine = null;
            while (null != (oneLine = bufferedReader.readLine())) {
                readResult.append(oneLine + "\n\r");
            }
            System.out.println(readResult.toString());
        } catch (IOException ioe) {
            //TODO log: IOException
            ioe.printStackTrace();
        }
    }


实现 AutoCloseable 接口

跟踪源码你会发现,使用 try-with-resources 语句自动关闭资源的类都实现了AutoCloseable 接口。


AutoCloseable 接口只有一个无参的close()方法,使用try-with-resources 语句声明的资源,只要实现了这个方法,就可以在抛出异常之前,调用close()方法进行资源关闭操作。


下面提供了一个使用线程池来执行任务的ExecutorServiceAutoCloseable,这个类实现了 AutoCloseable 接口的close()方法,可以在异常抛出以后,关闭线程池,从而达到释放线程资源的目的。


packagenet.ijiangtao.tech.designskill.trywithresources;
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
/*** AutoCloseable Thread pool** @author ijiangtao* @create 2019-05-13 13:08**/publicclassExecutorServiceAutoCloseableimplementsAutoCloseable {
privateExecutorServicepool;
privateintpoolSize;
publicExecutorServiceAutoCloseable() {
poolSize=Runtime.getRuntime().availableProcessors();
pool=Executors.newFixedThreadPool(poolSize);
    }
publicvoidexecute(Runnablerunnable) {
if (pool.isShutdown())
thrownewUnsupportedOperationException("pool isShutdown now");
pool.execute(runnable);
    }
@Overridepublicvoidclose() throwsException {
System.out.println("auto close now !!!!!!!!!!! ");
pool.shutdown();
    }
publicstaticvoidmain(String[] args) {
try (ExecutorServiceAutoCloseableexecutorServiceAutoCloseable=newExecutorServiceAutoCloseable();) {
executorServiceAutoCloseable.execute(newRunnable() {
@Overridepublicvoidrun() {
Integer.parseInt("test auto close");
                }
            });
        } catch (Exceptione) {
e.printStackTrace();
        }
    }
}


下面是输出的结果,可以看到在抛出异常之前,先执行了close()方法来关闭资源。

auto close now !!!!!!!!!!! 
Exception in thread "pool-1-thread-1" java.lang.NumberFormatException: For input string: "test auto close"
  at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
  at java.lang.Integer.parseInt(Integer.java:580)
  at java.lang.Integer.parseInt(Integer.java:615)
  at net.ijiangtao.tech.designskill.trywithresources.ExecutorServiceAutoCloseable$1.run(ExecutorServiceAutoCloseable.java:39)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  at java.lang.Thread.run(Thread.java:748)
复制代码

这样做的目的是,当程序因为异常而结束的时候,不需要显式地关闭线程池,而可以自动关闭,从而尽快释放线程资源,降低内存消耗。


这里要注意的是,程序结束就关闭线程池,这样做的好处是占用内存小,坏处是每次执行程序都要重新创建线程池,这是有一定性能消耗的。


因此要根据具体情况而定。通常,如果程序运行频繁,则保留线程池中线程,反复使用,减少因反复创建和销毁线程造成的性能消耗。而如果程序运行结束以后,短时间内不会再次运行,则可以将线程池关闭,释放掉占用的内存。


当然也可以通过设置核心线程数和最大线程数,以及过期时间来设置自己的线程管理策略。


具体用法可以参考这篇文章:使用ThreadPoolExecutor构造线程池


Java 9 final 变量

在Java 9 中,对 try-with-resources 语句的语法进行了进一步的精简。

如果你有一个资源是 final 或等效于 final 变量, 那么你可以在 try-with-resources 语句中使用该变量,而无需在 try-with-resources 语句中声明一个新变量。

Java 7 和 Java 8 中的写法:

privatestaticStringreadDataJava7(Stringmessage) throwsIOException {
Readerreader=newStringReader(message);
BufferedReaderbufferedReader=newBufferedReader(reader);
try (BufferedReaderbufferedReader2=bufferedReader) {
returnbufferedReader2.readLine();
    }
}

Java 9 支持的写法:

privatestaticStringreadDataJava9(Stringmessage) throwsIOException {
Readerreader=newStringReader(message);
BufferedReaderbufferedReader=newBufferedReader(reader);
try (bufferedReader) {
returnbufferedReader.readLine();
        }
    }


总结

通过  try-with-resources 语句的的好处可以总结如下:

  1. try-with-resources 语句可以带来更加简洁的代码;
  2. try-with-resources 语句可以使得资源释放更加安全;
  3. 自己实现 AutoCloseable 接口并使用 try-with-resources 语句可以实现安全简洁的资源释放。
目录
相关文章
|
7月前
|
Java API 开发工具
【Azure Developer】Java代码实现获取Azure 资源的指标数据却报错 "invalid time interval input"
在使用 Java 调用虚拟机 API 获取指标数据时,因本地时区设置非 UTC,导致时间格式解析错误。解决方法是在代码中手动指定时区为 UTC,使用 `ZoneOffset.ofHours(0)` 并结合 `withOffsetSameInstant` 方法进行时区转换,从而避免因时区差异引发的时间格式问题。
336 4
|
10月前
|
消息中间件 机器学习/深度学习 Java
java 最新技术驱动的智能教育在线实验室设备管理与实验资源优化实操指南
这是一份基于最新技术的智能教育在线实验室设备管理与实验资源优化的实操指南,涵盖系统搭建、核心功能实现及优化策略。采用Flink实时处理、Kafka消息队列、Elasticsearch搜索分析和Redis缓存等技术栈,结合强化学习动态优化资源调度。指南详细描述了开发环境准备、基础组件部署、数据采集与处理、模型训练、API服务集成及性能调优步骤,支持高并发设备接入与低延迟处理,满足教育机构数字化转型需求。代码已提供下载链接,助力快速构建智能化实验室管理系统。
245 44
|
Java 程序员
JAVA程序员的进阶之路:掌握URL与URLConnection,轻松玩转网络资源!
在Java编程中,网络资源的获取与处理至关重要。本文介绍了如何使用URL与URLConnection高效、准确地获取网络资源。首先,通过`java.net.URL`类定位网络资源;其次,利用`URLConnection`类实现资源的读取与写入。文章还提供了最佳实践,包括异常处理、连接池、超时设置和请求头与响应头的合理配置,帮助Java程序员提升技能,应对复杂网络编程场景。
311 9
|
9月前
|
机器学习/深度学习 Java 大数据
Java 大视界 -- Java 大数据在智能政务公共资源交易数据分析与监管中的应用(202)
本篇文章深入探讨了 Java 大数据在智能政务公共资源交易监管中的创新应用。通过构建高效的数据采集、智能分析与可视化决策系统,Java 大数据技术成功破解了传统监管中的数据孤岛、效率低下和监管滞后等难题,为公共资源交易打造了“智慧卫士”,助力政务监管迈向智能化、精准化新时代。
|
Java
Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
【10月更文挑战第14天】Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
341 2
|
人工智能 监控 安全
Java智慧工地(源码):数字化管理提升施工安全与质量
随着科技的发展,智慧工地已成为建筑行业转型升级的重要手段。依托智能感知设备和云物互联技术,智慧工地为工程管理带来了革命性的变革,实现了项目管理的简单化、远程化和智能化。
349 5
|
SQL 安全 Java
Java 异常处理:筑牢程序稳定性的 “安全网”
本文深入探讨Java异常处理,涵盖异常的基础分类、处理机制及最佳实践。从`Error`与`Exception`的区分,到`try-catch-finally`和`throws`的运用,再到自定义异常的设计,全面解析如何有效管理程序中的异常情况,提升代码的健壮性和可维护性。通过实例代码,帮助开发者掌握异常处理技巧,确保程序稳定运行。
391 2
|
Java 开发者
JAVA高手必备:URL与URLConnection,解锁网络资源的终极秘籍!
在Java网络编程中,URL和URLConnection是两大关键技术,能够帮助开发者轻松处理网络资源。本文通过两个案例,深入解析了如何使用URL和URLConnection从网站抓取数据和发送POST请求上传数据,助力你成为真正的JAVA高手。
316 11