在Java编程过程中,如果打开了外部资源(文件、数据库连接、网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们。因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制,如果我们不在编程时确保在正确的时机关闭外部资源,就会导致外部资源泄露,紧接着就会出现文件被异常占用,数据库连接过多导致连接池溢出等诸多很严重的问题。
在java1.7以前,我们关闭资源的方式如下:
public class CloseTest {
public static void main(String[] args){
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream("file.txt");
fileInputStream.read();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fileInputStream != null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
为了确保外部资源一定要被关闭,通常关闭代码被写入finally代码块中,关闭资源时可能抛出的异常,于是经典代码就诞生了。
但是,在java1.7版本之后,新增加了一个语法糖,就是try-with-resources语句,我们先直接上一个demo,方便理解:
public class CloseTest {
public static void main(String[] args) {
try (FileInputStream fileInputStream1 = new FileInputStream("file1.txt");
FileInputStream fileInputStream2 = new FileInputStream("file2.txt")) {
fileInputStream1.read();
fileInputStream2.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
将外部资源的句柄对象的创建放在try关键字后面的括号中,当这个try-catch代码块执行完毕后,Java会确保外部资源的close方法被调用。多个语句使用分号断开。
反编译之后我们可以看见:
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);
}
}
通过反编译的代码,大家可能注意到代码中有一处对异常的特殊处理:
var2.addSuppressed(var11);
这是try-with-resource语法涉及的另外一个知识点,叫做异常抑制。当对外部资源进行处理(例如读或写)时,如果遭遇了异常,且在随后的关闭外部资源过程中,又遭遇了异常,那么你catch到的将会是对外部资源进行处理时遭遇的异常,关闭资源时遭遇的异常将被“抑制”但不是丢弃,通过异常的getSuppressed方法,可以提取出被抑制的异常。
源码里面有解释:
/**
* Returns an array containing all of the exceptions that were
* suppressed, typically by the {@code try}-with-resources
* statement, in order to deliver this exception.
*
* If no exceptions were suppressed or {@linkplain
* #Throwable(String, Throwable, boolean, boolean) suppression is
* disabled}, an empty array is returned. This method is
* thread-safe. Writes to the returned array do not affect future
* calls to this method.
*
* @return an array containing all of the exceptions that were
* suppressed to deliver this exception.
* @since 1.7
*/
public final synchronized Throwable[] getSuppressed() {
if (suppressedExceptions == SUPPRESSED_SENTINEL ||
suppressedExceptions == null)
return EMPTY_THROWABLE_ARRAY;
else
return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY);
}
因为不管什么情况下(异常或者非异常)资源都必须关闭,在jdk1.6之前,应该把close()放在finally块中,以确保资源的正确释放。如果使用jdk1.7以上的版本,推荐使用try-with-resources语句。