自动调用关闭释放资源try-with-resources
try-with-resources自动执行释放资源
看到了try这个关键字立马就应该能想到异常处理机制try-catch-finally语句块。这里要说的东西和异常处理背后的机制其实几乎是一样的,只不过try-with-resources侧重点异常后自动释放资源,不需要我们手动去在调用close()方法。后面会从JLS层面,以及从字节码层面举例详细说明。
做什么用的
try-with-resources语句是用变量(称为资源)来参数化的,这些变量在try块执行之前被初始化,并在try块执行后以初始化的相反顺序自动关闭。当资源自动关闭时,catch子句和finally子句通常是不必要的。
一句话就是用来自动调用关闭方法,释放资源。
Java语言规范(JLS)是如何定义的
- 在资源规范中声明的变量类型必须是AutoCloseable的子类型,否则会发生编译时错误
- 多个资源使用';'号分割,也就说try语句块中可以写多个资源初始化。
- 不能有重名的变量名,否则编译报错
- 在资源规范中声明的变量如果没有显式声明为final,则隐式声明为final
有哪些使用姿势
- 基本使用方法,只需try(){}语句块即可
public class FileWriter implements AutoCloseable{ @Override public void close() { System.out.println("file writer close"); } public void writeFile(String s) { System.out.println("file writer write" + s); } } //这里举例一个类实现了AutoCloseable接口,然后,我们就可以直接使用try资源语句块来包装它, //使用完不需要去手动去close(),会自动调用close方法。 try ( FileWriter writer = new FileWriter(); ) { writer.writeFile("target.cs"); } //多个子句也是一样,关闭的顺序是反着来。如下会先调用reader.close(),然后是writer.close() try ( FileWriter writer = new FileWriter(); FileReader reader = new FileReader(); ) { writer.writeFile("target.cs"); reader.readFile("target.cs"); }
- 可以结合catch,finally像异常处理一样。嵌套什么的,也没问题
try (FileWriter writer = new FileWriter()) { writer.writeFile("target.cs"); try(FileReader reader = new FileReader()) { reader.readFile("target.cs"); } }catch (Exception e) { //do exception }
从字节码层面详细说明JVM是如何解析和执行的
我们没有主动调用close()方法,那是何时谁调用的close呢,我们从编译后的字节码中就可以看到
假设源码如下:非常的简单,FileWriter这个类没有贴出,就是像上面的一样实现了AutoCloseable接口,然后自定义了一个writeFile方法。
public class AutoClose {
public static void main(String[] args) {
try (FileWriter writer = new FileWriter()) {
writer.writeFile("target.cs");
}
}
}
编译后字节码展示如下:
重点说明:
我这里按照字节码序号,也就是自然顺序号(也就是第一列)[第二列是PC号]
- 可以看到第15行出现了一句,invokevirtual 字节码指令,调用了FileWriter.close()方法。
- 另外第23行也出现了一模一样的字节码指令。这是为了处理即使异常情况下,close方法也要正常调用。
- 如下是异常表:
也就是说,即使调用writeFile方法异常了,也要自动调用关闭方法,所以可以看到异常表PC号是814。异常表的第二行,解释大白话就是如果close方法异常了,那就把异常加入到异常列表中,因此看到的PC号是2226。最终是将异常抛出。 - 粉色的+19、+9那些表示的是字节码偏移量 (当前PC号加括号中的)
多个资源列表的时候等于是多个catch一样,只要初始化成功的,无论执行业务方法是否异常,都会调用它的close方法。
try-with-resources优先于try-finally
这个原则在Effective Java中也有说明。尤其是当语句块中有多个资源需要处理关闭操作的时候,嵌套try-finally语句,很可能会导致异常被抹除。而使用try-with-resources即使多个异常被禁止时,仍然可以使用getSuppresed方法访问到它们