c++ try catch语句_Java语法糖 : 使用 try-with-resources 语句安全地释放资源

news/2024/7/7 8:16:02

v2-4a5f110b7e51196fffaae339d2b03371_1440w.jpg?source=172ae18b

先给出本文的重点:

  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操作。

public static void method1() {

    FileWriter fileWriter = null;
    BufferedWriter bufferedWriter = null;
    FileReader fileReader = null;
    BufferedReader bufferedReader = null;
    File file = new File("try-with-resources-demo.txt");

    try {


        fileWriter = new FileWriter(file);
        bufferedWriter = new BufferedWriter(fileWriter);

        fileReader = new FileReader(file);
        bufferedReader = new BufferedReader(fileReader);

        bufferedWriter.write("now is:" + LocalDateTime.now() + "nr");
        bufferedWriter.write("availableProcessors are : " + Runtime.getRuntime().availableProcessors() + "nr");
        bufferedWriter.write("totalMemory is : " + Runtime.getRuntime().totalMemory() + "nr");
        bufferedWriter.write("maxMemory is : " + Runtime.getRuntime().maxMemory() + "nr");
        bufferedWriter.write("freeMemory is : " + Runtime.getRuntime().freeMemory() + "nr");
        bufferedWriter.flush();

        StringBuffer readResult = new StringBuffer("");
        String oneLine = null;
        while (null != (oneLine = bufferedReader.readLine())) {
            readResult.append(oneLine + "nr");
        }
        System.out.println(readResult.toString());


    } catch (IOException ioe) {
        //TODO log: IOException
        ioe.printStackTrace();
    } finally {
        try {
            if (null != fileReader)
                fileReader.close();
        } catch (IOException ioe) {
            //TODO log: close stream has an  IOException
            ioe.printStackTrace();
        }

        try {
            if (null != bufferedReader)
                bufferedReader.close();
        } catch (IOException ioe) {
            //TODO log: close stream has an  IOException
            ioe.printStackTrace();
        }

        try {
            if (null != fileWriter)
                fileWriter.close();
        } catch (IOException ioe) {
            //TODO log: close stream has an  IOException
            ioe.printStackTrace();
        }

        try {
            if (null != bufferedWriter)
                bufferedWriter.close();
        } catch (IOException ioe) {
            //TODO log: close stream has an  IOException
            ioe.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() + "nr");
            bufferedWriter.write("availableProcessors are : " + Runtime.getRuntime().availableProcessors() + "nr");
            bufferedWriter.write("totalMemory is : " + Runtime.getRuntime().totalMemory() + "nr");
            bufferedWriter.write("maxMemory is : " + Runtime.getRuntime().maxMemory() + "nr");
            bufferedWriter.write("freeMemory is : " + Runtime.getRuntime().freeMemory() + "nr");
            bufferedWriter.flush();


            StringBuffer readResult = new StringBuffer("");
            String oneLine = null;
            while (null != (oneLine = bufferedReader.readLine())) {
                readResult.append(oneLine + "nr");
            }
            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()方法,可以在异常抛出以后,关闭线程池,从而达到释放线程资源的目的。

package net.ijiangtao.tech.designskill.trywithresources;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * AutoCloseable Thread pool
 *
 * @author ijiangtao
 * @create 2019-05-13 13:08
 **/
public class ExecutorServiceAutoCloseable implements AutoCloseable {

    private ExecutorService pool;
    private int poolSize;

    public ExecutorServiceAutoCloseable() {
        poolSize = Runtime.getRuntime().availableProcessors();
        pool = Executors.newFixedThreadPool(poolSize);
    }

    public void execute(Runnable runnable) {
        if (pool.isShutdown())
            throw new UnsupportedOperationException("pool isShutdown now");
        pool.execute(runnable);
    }

    @Override
    public void close() throws Exception {
        System.out.println("auto close now !!!!!!!!!!! ");
        pool.shutdown();
    }

    public static void main(String[] args) {
        try (ExecutorServiceAutoCloseable executorServiceAutoCloseable = new ExecutorServiceAutoCloseable();) {
            executorServiceAutoCloseable.execute(new Runnable() {
                @Override
                public void run() {
                    Integer.parseInt("test auto close");
                }
            });
        } catch (Exception e) {
            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 中的写法:

private static String readDataJava7(String message) throws IOException {
    Reader reader = new StringReader(message);
    BufferedReader bufferedReader = new BufferedReader(reader);
    try (BufferedReader bufferedReader2 = bufferedReader) {
        return bufferedReader2.readLine();
    }
}

Java 9 支持的写法:

    private static String readDataJava9(String message) throws IOException {
        Reader reader = new StringReader(message);
        BufferedReader bufferedReader = new BufferedReader(reader);
        try (bufferedReader) {
            return bufferedReader.readLine();
        }
    }

总结

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

  1. try-with-resources 语句可以带来更加简洁的代码
  2. try-with-resources 语句可以使得资源释放更加安全
  3. 自己实现 AutoCloseable 接口并使用 try-with-resources 语句可以实现安全简洁的资源释放

v2-4a3486e5600f3e15b781c8142a7fac20_b.jpg

http://www.niftyadmin.cn/n/3661325.html

相关文章

[瞭望站] 客户同我们一起成长。。。

2006年09月01日 15:46:00 最近在上海,接连走访了2个客户,拿到了3套客户需求,给他们演示了楼上集成开发方法,他们也表示了浓重的兴趣。现在的客户,被忽悠得多了,所有现在变得特别务实和谨慎,当我…

列举windows通过编程启动exe程序的方法?_一些简单的持久性后门的方法

一,shift后门方法1:修改注册表-映像劫持REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe" /v Debugger /t REG_SZ /d "C:\windows\system32\cmd.exe"命令说明:reg …

[需求]需求分析能力之样例:引入领域模型的前前后后

2006年07月27日 13:50:00 需求分析能力之样例:引入领域模型的前前后后曾经遇到过一个系统需求,需求分析人员在听到客户说要增加"修改员工密码"功能后,就原封不动的把这个功能写在了文档中。如果把这个需求交给实现人员,…

thinkphp漏洞_CNNVD关于ThinkPHP远程代码执行漏洞情况的通报

近日,国家信息安全漏洞库(CNNVD)收到关于ThinkPHP 5.0远程代码执行漏洞(CNNVD-201901-445)【http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVDCNNVD-201901-445】情况的报送。成功利用此漏洞的攻击者可以对目标系统进行远程代码执行攻击。ThinkPHP 5.0.x–5.0.…

[领域]一个简单的UML应用题

2006年07月26日 15:08:00 UML是一个语义丰富的建模语言,但是,在实际的使用过程中,出现了很多滥用和误用的情况。如果,你只是关心使用UML的最少集合进行建模,是可以的(我个…

内存都是由半导体器件构成的_Linux 内存相关问题汇总

linux 内存是后台开发人员,需要深入了解的计算机资源。合理的使用内存,有助于提升机器的性能和稳定性。本文主要介绍 linux 内存组织结构和页面布局,内存碎片产生原因和优化算法,linux 内核几种内存管理的方法,内存使用…

tensorboard可视化_TensorBoard可视化实战(三)

概述在《TensorBoard可视化实战(二)》中讲述了如何使生成的图更具有可阅读性,但是生成的可阅读参数较少,本文旨在增加TensorBoard的展示信息。mnist_board_2.py代码import osimport tensorflow as tfLOGDIR ./mnistmnist tf.contrib.learn.datasets.mn…

[个人]百度裁员录音门

2006年07月17日 20:32:00 手头有个录音,朋友给我的,说:"听听,百度裁员的,特无耻"。耐心听完,第一感觉,是骂我的朋友,"怎么能说无耻呢?玷污了无耻这个词了…