java中的AutoCloseable接口与try with resources

try-with-resources引入的背景

很多Java程序都需要操作一些资源,如文件、流(streams)、套接字(sockets)和数据库连接(database connections)。操作这些资源的时候得很小心,因为它们操作的时候获取了操作系统的资源。所以你得保证,即使在发生错误的情况下也要把它们占用的操作系统资源释放掉。事实上,不正确的资源管理是导致生产环境应用中发生错误的常见原因,比如代码中发生了异常,而数据库连接和文件描述符(file descriptors)还是打开状态,这样将频繁导致服务器资源耗尽的时候需要重启,因为操作系统和服务器通常都有资源的上限限制。
正确的操作方式是,操作资源完成后调用其close()方法。典型的使用方式是try/catch/finally代码块,它将保证finally中的close方法总能被调用。然而代码中会充斥着大量try/catch/finally类似的代码块。一些其它的编程语言,比如Python、Ruby都提供了语言级的特性,比如自动资源管理来解决这种问题。
Java7引入了try-with-resources表达式来减少try/catch/finally样板代码,代码如下:

  //注意try后面的括号中,如果打开了多个资源,要用分号隔开
  try (
       FileOutputStream out = new FileOutputStream("output");
       FileInputStream  in1 = new FileInputStream(“input1”);
       FileInputStream  in2 = new FileInputStream(“input2”)
   ) {
       //这儿可以对三个流进行操作
   }   // out, in1 和 in2 将总会被关闭,不管是否发生异常

AutoCloseable接口

上面的try-with-resources中的try后面的括号中的资源类必须得实现AutoCloseable接口,这样就知道它们肯定有一个close()方法以供调用。java.lang.AutoCloseable接口是从Java SE7开始引入的,它仅仅提供了一个void方法close(),该方法可能抛出一个受检异常(Checked Exception)。任何想要使用try-with-resources的资源类都必须实现AutoCloseable接口,并且强烈推荐各个资源类提供更详细的异常类,而不是笼统的java.lang.Exception,如果确定不可能有异常抛出,也可以在重写close()方法的时候不抛出异常。
AutoCloseable接口有一个子接口java.io.Closeable。注意AutoCloseable位于java.lang包下,而Closeable位于java.io包下面。区别在于AutoCloseable针对的是任何资源,不仅仅是I/O,它不要求close()方法是幂等的,也就是说多次调用close()方法可能会有副作用。而Closeable接口是用于关闭I/O流的,java.io.InputStreamjava.io.OutputStream这两个抽象类都实现了Closeable接口,Closeable接口的close()方法要求是幂等的,多次调用不产生副作用。

try-with-resources语法糖揭秘

运行如下代码:

package com.pilaf;

import java.util.Arrays;

/**
 * @description:
 * @author: pilaf
 * @create: 2018-10-20 21:20
 */
public class TestAutoCloseable {

    public static void main(String[] args) {
        //try后面小括号中的资源类都要实现AutoCloseable接口
        try (ResourceOperation operation = new ResourceOperation()) {
            System.out.println("使用资源类进行操作!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}


class ResourceOperation implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("资源关闭了!");
    }
}

控制台输出:

使用资源类进行操作!
资源关闭了!

可见,在资源操作完毕后,它的close()方法被自动调用。
将生成的Java字节码文件反编译后,看到如下的代码:

package com.pilaf;

import java.io.PrintStream;

public class TestAutoCloseable
{
  public static void main(String[] paramArrayOfString)
  {
    try
    {
      ResourceOperation localResourceOperation = new ResourceOperation();
      Object localObject1 = null;
      try
      {
        System.out.println("使用资源类进行操作!");
      }
      catch (Throwable localThrowable2)
      {
      //先把localThrowable2赋值给localObject1,以便在finally块中记录
        localObject1 = localThrowable2;
        throw localThrowable2;
      }
      finally
      {
        if (localResourceOperation != null) {
          if (localObject1 != null) {
            try
            {
              localResourceOperation.close();
            }
            catch (Throwable localThrowable3)
            {
              //关闭资源的时候,调用其close方法抛出的异常被添加为Suppressed异常了!
              ((Throwable)localObject1).addSuppressed(localThrowable3);
            }
          } else {
            localResourceOperation.close();
          }
        }
      }
    }
    catch (Exception localException)
    {
      localException.printStackTrace();
    }
  }
}

package com.pilaf;

import java.io.PrintStream;
import java.util.Arrays;

class ResourceOperation
  implements AutoCloseable
{
  public void close()
    throws Exception
  {
    System.out.println("资源关闭了!");
  }
}

可见,其实编译好的字节码文件中还是用try/catch/finally代码块实现的。

参考网址:
https://www.oracle.com/technetwork/articles/java/trywithresources-401775.html

你可能感兴趣的:(java)