Java7新特性:try-with-resources自动关闭资源

Catalogue
  1. 1. try-with-resources
    1. 1.1. 概念
    2. 1.2. 示例代码
    3. 1.3. 异常测试
  2. 2. Closeable和AutoColseable接口

try-with-resources是Java7中新增特性,它允许在try关键字后跟一对圆括号,括号内可初始化一个或多个资源,try语句会在代码块执行完后自动关闭资源。

try-with-resources

概念

在Java7之前,某些稀缺资源(如数据库连接、网络连接,IO流)使用后必须在finally语句中手动关闭,否则可能造成资源泄露、浪费,资源使用一般流程如下形式:

1
2
3
4
5
6
7
try {
//资源使用
} catch(Exception e) {
//异常处理
} finally {
//关闭资源
}

为解决以上问题,Java7中新增try-with-resources特性。它允许在try关键字后跟一对圆括号,括号内可初始化一个或多个资源,try语句会在代码块执行完后自动关闭资源。资源使用流程如下:

1
2
3
4
5
try (Resource res = ) {
//使用资源
} catch (Exception e) {
//异常处理
}

try-with-resources特性会在try代码块执行完后自动关闭所有资源,无需在finally中显示调用close()方法手动关闭,一来简洁了代码,二来以免忘记关闭。
try-with-resource也可以有catch和finally块,只是执行时机是在try-with-resource之后。

为保证try能正常自动关闭资源,要求资源:

  1. 被关闭资源类必须实现autoClosable接口或者是Closable接口
  2. 需要自动关闭的资源必须在try后面圆括号内声明
  3. 资源自动关闭顺序与声明顺序相反

示例代码

使用实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class AutoCloseTest {
@Test
public void testTryWithResource() {
try(Resource1 re1 = new Resource1(); Resource2 re2 = new Resource2()) {
re1.doThing();
re2.doThing();
} catch (Exception e) {
e.printStackTrace();
}
}
class Resource1 implements AutoCloseable {
public void doThing() {
System.out.println("resource1 do something");
}
@Override
public void close() throws Exception {
System.out.println("close resource1");
}
}
class Resource2 implements AutoCloseable {
public void doThing() {
System.out.println("resource2 do something");
}
@Override
public void close() throws Exception {
System.out.println("close resource2");
}
}
}

执行结果:

1
2
3
4
resource1 do something
resource2 do something
close resource2
close resource1

可见资源关闭顺序与初始化顺序相反。

异常测试

Java7 以前如果try语句块和finally同时都抛出异常则只会抛出 finally 块中的异常,不会抛出try块中异常。这样经常会导致得到的异常信息不是调用程序想要得到的。

Java7 及以后版本中如果采用try-with-resource机制,如果在try-with-resource 声明中抛出异(可能是文件无法打或都文件无法关闭),同事try语句块中也抛出异常,则首先抛出try块中异常,同时try-with-resource抛出的异常会使用addSuppressed方法,将之前的异常记录下来。我们可以在自己的代码中调用getSuppressed方法去获取异常列表并处理。

采用try-with-resource机制抛异常测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testThrowException2() throws Exception {
try (Resource3 re3 = new Resource3()) {
re3.doThing();
}
}
class Resource3 implements AutoCloseable {
public void doThing() throws Exception {
throw new Exception("doThing throw exception");
}
@Override
public void close() throws Exception {
throw new Exception("close throw exception");
}
}

执行结果:

1
2
3
4
5
6
7
java.lang.Exception: doThing throw exception
at com.zxy.lab.code.test.AutoCloseTest$Resource3.doThing(AutoCloseTest.java:61)
at com.zxy.lab.code.test.AutoCloseTest.testThrowException(AutoCloseTest.java:50)
...
Suppressed: java.lang.Exception: close throw exception
at com.zxy.lab.code.test.AutoCloseTest$Resource3.close(AutoCloseTest.java:66)
at com.zxy.lab.code.test.AutoCloseTest.testThrowException(AutoCloseTest.java:52)

从以上异常信息可以看到,对外抛出的异常时try语句块中的异常,同时try-with-resource抛出的异常会使用addSuppressed方法,将之前的异常记录下来。我们可以在自己的代码中调用getSuppressed方法去获取异常列表并处理,所以这才是我们想要的结果。

1
2
3
for (Throwable throwable : e.getSuppressed()) {
System.out.println(throwable.getMessage());
}

Java7之前同时抛出异常测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Test
public void testThrowException3() throws Exception {
Resource3 re3 = null;
try {
re3 = new Resource3();
re3.doThing();
} finally {
if (re3 != null) {
re3.close();
}
}
}
class Resource3 implements AutoCloseable {
public void doThing() throws Exception {
throw new Exception("doThing throw exception");
}
@Override
public void close() throws Exception {
throw new Exception("close throw exception");
}
}

结果:

1
2
3
4
5
6
7
8
java.lang.Exception: close throw exception
at com.zxy.lab.code.test.AutoCloseTest$Resource3.close(AutoCloseTest.java:106)
at com.zxy.lab.code.test.AutoCloseTest.testThrowException3(AutoCloseTest.java:92)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
...

可见,对外只抛出了finally块中异常,这不是我们想要的。

Closeable和AutoColseable接口

Closeable接口是jdk5中添加的,AutoColseable则是jdk7才添加的:

1
2
3
4
5
6
7
8
9
10
11
* @since 1.5
*/
public interface Closeable extends AutoCloseable
* @since 1.7
*/
public interface AutoCloseable
/**
* Closes this resource, relinquishing any underlying resources.
* This method is invoked automatically on objects managed by the
* {@code try}-with-resources statement.

AutoCloseable接口对Java7中新增特性try-with-resources提供了支持,只有实现了AutoCloseable接口的类的对象才可以由带资源的try语句进行管理。
这个两个接口都只有一个close()方法。

1
void close() throws Exception

通过这个方法关闭调用对象,释放可能占用的所有资源。try-with-resources会自动调用该方法关闭资源。

Closeable接口也定义了close()方法。实现了Closeable接口的类的对象可以被关闭。从Java7开始,Closeable扩展了AutoCloseable。因此,在Java7中,所有实现了Closeable接口的类也都实现了AutoCloseable接口。

Comments