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.InputStream
和java.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