Java如何优雅地关闭资源try-with-resource及其异常抑制

一、背景

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

 二、传统的资源关闭方式

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

public static void main(String[] args) {
  FileInputStream inputStream = null;
  try {
    inputStream = new FileInputStream(new File("test"));
    System.out.println(inputStream.read());
  } catch (IOException e) {
    throw new RuntimeException(e.getMessage(), e);
  } finally {
    if (inputStream != null) {
      try {
        inputStream.close();
      } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
      }
    }
  }
}

熟悉其他语言的朋友可能会开始吐槽了,在C++中,我们可以把关闭资源的代码放在析构函数中,在C#中,我们有using代码块。这些语法都有一个共同的特性,让外部资源的关闭行为与外部资源的句柄对象的生命周期关联,当外部资源的句柄对象生命周期终结时(例如句柄对象已出作用域),外部资源的关闭行为将被自动调用。这样不仅更加符合面向对象的编程理念(将关闭外部资源的行为内聚在外部资源的句柄对象中),也让代码更加简洁易懂。怎么到了Java这里,就找不到自动关闭外部资源的语法特性了呢。

三、JDK7及其之后的资源关闭方式

3.1 try-with-resource语法

确实,在JDK7以前,Java没有自动关闭外部资源的语法特性,直到JDK7中新增了try-with-resource语法,才实现了这一功能。

那什么是try-with-resource呢?简而言之,当一个外部资源的句柄对象(比如FileInputStream对象)实现了AutoCloseable接口,那么就可以将上面的板式代码简化为如下形式:

public static void main(String[] args) {
  try (FileInputStream inputStream = new FileInputStream(new File("test"))) {
    System.out.println(inputStream.read());
  } catch (IOException e) {
    throw new RuntimeException(e.getMessage(), e);
  }
}

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

3.2 实现原理

try-with-resource并不是JVM虚拟机的新增功能,只是JDK实现了一个语法糖,当你将上面代码反编译后会发现,其实对JVM虚拟机而言,它看到的依然是之前的写法:

public static void main(String[] args) {
  try {
    FileInputStream inputStream = new FileInputStream(new File("test"));
    Throwable var2 = null;

    try {
      System.out.println(inputStream.read());
    } catch (Throwable var12) {
      var2 = var12;
      throw var12;
    } finally {
      if (inputStream != null) {
        if (var2 != null) {
          try {
            inputStream.close();
          } catch (Throwable var11) {
            var2.addSuppressed(var11);
          }
        } else {
          inputStream.close();
        }
      }

    }

  } catch (IOException var14) {
    throw new RuntimeException(var14.getMessage(), var14);
  }
}

3.3 异常抑制

通过反编译的代码,大家可能注意到代码中有一处对异常的特殊处理:

var2.addSuppressed(var11);

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

四、总结

1、当一个外部资源的句柄对象实现了AutoCloseable接口,JDK7中便可以利用try-with-resource语法更优雅的关闭资源,消除板式代码。

2、try-with-resource时,如果对外部资源的处理和对外部资源的关闭均遭遇了异常,“关闭异常”将被抑制,“处理异常”将被抛出,但“关闭异常”并没有丢失,而是存放在“处理异常”的被抑制的异常列表中。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 深入理解Java基础之try-with-resource语法糖

    背景 众所周知,所有被打开的系统资源,比如流.文件或者Socket连接等,都需要被开发者手动关闭,否则随着程序的不断运行,资源泄露将会累积成重大的生产事故. 在Java的江湖中,存在着一种名为finally的功夫,它可以保证当你习武走火入魔之时,还可以做一些自救的操作.在远古时代,处理资源关闭的代码通常写在finally块中.然而,如果你同时打开了多个资源,那么将会出现噩梦般的场景: public class Demo { public static void main(String[] arg

  • Java如何优雅地关闭资源try-with-resource及其异常抑制

    一.背景 我们知道,在Java编程过程中,如果打开了外部资源(文件.数据库连接.网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们.因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制,如果我们不在编程时确保在正确的时机关闭外部资源,就会导致外部资源泄露,紧接着就会出现文件被异常占用,数据库连接过多导致连接池溢出等诸多很严重的问题.  二.传统的资源关闭方式 为了确保外部资源一定要被关闭,通常关闭代码被写入finally代码块中,当然我们还必须注意到关闭资源时可能抛出的异常,于是变

  • 如何更优雅的关闭java文本、网络等资源

    通常在 java 中对文本.网络资源等操作起来是很繁杂的,要声明,读取,关闭三个阶段,还得考虑异常情况.假设我们要读取一段文本显示到控制台,通常会有如下的代码: public static void main(String[] args) { FileInputStream inputStream = null; try { inputStream = new FileInputStream("./pom.xml"); InputStreamReader inputStreamRead

  • 浅谈java7增强的try语句关闭资源

    java7增强的try语句关闭资源 传统的关闭资源方式 import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; class Student implements Serializable { private String name; public Student(String name)

  • Java使用 try-with-resources 实现自动关闭资源的方法

    1. 在Java1.7之前,我们需要通过下面这种方法, 在finally中释放资源,这种方法有点繁琐. BufferedReader br = null; String str; try { br = new BufferedReader(new FileReader("")); while ((str = br.readLine()) != null) { System.out.println(str); } } catch (IOException e) { e.printStac

  • java实现HttpClient异步请求资源的方法

    本文实例讲述了java实现HttpClient异步请求资源的方法.分享给大家供大家参考.具体实现方法如下: package demo; import java.util.concurrent.CountDownLatch; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.nio.client.DefaultHttpAsyn

  • java实现Runnable接口适合资源的共享

    本文为大家分享了java实现Runnable接口适合资源的共享,供大家参考,具体内容如下 Java当中,创建线程通常用两种方式: 1.继承Thread类 2.实现Runnable接口 但是在通常的开发当中,一般会选择实现Runnable接口,原因有二: 1.避免单继承的局限,在Java当中一个类可以实现多个接口,但只能继承一个类 2.适合资源的共享 原因1我们经常听到,但是2是什么呢?下面用一个例子来解释: 有5张票,分两个窗口卖: 继承Thread类: public class ThreadD

  • Java微信支付之关闭订单

    本文实例为大家分享了java微信支付之关闭订单的具体代码,供大家参考,具体内容如下 官方文档 一.应用场景 商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付 系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口 注意:订单生成后不能马上调用关单接口,最短调用时间间隔为5分钟. 二.接口地址 https://api.mch.weixin.qq.com/pay/closeorder 三.请求参数 只能根据自己商户系统的订单号关闭 package co

  • Java map 优雅的元素遍历方式说明

    Java 8 , Lambda + foreach 语法糖, 写起来非常的 clean public static void main(String[] args) { // map init Map<String, String> map = new HashMap<>(); map.put("k", "v"); /*=====处理函数只有单条语句=====*/ map.forEach((k, v) -> System.out.pri

  • java后台实现js关闭本页面,父页面指定跳转或刷新操作

    关闭本页面,跳转到百度 response.setCharacterEncoding("gbk"); PrintWriter out=response.getWriter(); out.print("<script>alert('报告生成成功);</script>"); out.print("<script>self.opener.location.href = 'http://www.baidu.com';window.

  • Java Gradle项目中的资源正确获取方式

    引言 一个Java Gradle项目会涉及到资源的访问. 一般情况下会将当前项目所需的资源文件全部放置于resources文件夹下, 无论是main文件下的source code 还是test文件夹下的test code. 都或多或少的涉及到获取resources文件夹下的资源. 本文主要目的就是详细的总结一下如何获取resources文件夹下的资源. 两个getResource方法 来看一个简单的Java Gradle项目(称呼其为simpleresource)的项目结构 首先这个projec

随机推荐