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 语句可以实现安全简洁的资源释放。
目录
相关文章
|
25天前
|
Java 程序员
JAVA程序员的进阶之路:掌握URL与URLConnection,轻松玩转网络资源!
在Java编程中,网络资源的获取与处理至关重要。本文介绍了如何使用URL与URLConnection高效、准确地获取网络资源。首先,通过`java.net.URL`类定位网络资源;其次,利用`URLConnection`类实现资源的读取与写入。文章还提供了最佳实践,包括异常处理、连接池、超时设置和请求头与响应头的合理配置,帮助Java程序员提升技能,应对复杂网络编程场景。
49 9
|
1月前
|
Java
Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
【10月更文挑战第14天】Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
59 2
|
15天前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
34 4
|
25天前
|
Java 开发者
JAVA高手必备:URL与URLConnection,解锁网络资源的终极秘籍!
在Java网络编程中,URL和URLConnection是两大关键技术,能够帮助开发者轻松处理网络资源。本文通过两个案例,深入解析了如何使用URL和URLConnection从网站抓取数据和发送POST请求上传数据,助力你成为真正的JAVA高手。
46 11
|
18天前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
38 2
|
1月前
|
安全 Java 编译器
Java 泛型深入解析:类型安全与灵活性的平衡
Java 泛型通过参数化类型实现了代码重用和类型安全,提升了代码的可读性和灵活性。本文深入探讨了泛型的基本原理、常见用法及局限性,包括泛型类、方法和接口的使用,以及上界和下界通配符等高级特性。通过理解和运用这些技巧,开发者可以编写更健壮和通用的代码。
|
2月前
|
安全 Java API
java安全特性
java安全特性
29 8
|
2月前
|
自然语言处理 安全 Java
Java 语法糖是什么?
语法糖是一种编程语言的设计概念,旨在通过更简洁、易读的方式表示某些操作,提升代码可读性和减少错误。它不增加语言功能,而是简化代码。Java中的语法糖包括自动装箱与拆箱、增强型for循环、泛型、可变参数、try-with-resources、Lambda表达式、方法引用、字符串连接、Switch表达式和类型推断等,这些特性使Java代码更为简洁易读。
91 23
|
2月前
|
安全 Java API
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
68 11
|
2月前
|
安全 Java API
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
99 11
下一篇
无影云桌面