在 Java 7 之前,程序中如果有须要敞开的资源,例如 java.io.InputStreamjava.sql.Connection 等,通常会在 finally 中敞开,例如:

InputStream inputStream = null;try {    inputStream = new FileInputStream("/my/file");    // ...} catch (Exception e) {    e.printStackTrace();} finally {    if (inputStream != null) {        try {            inputStream.close();        } catch (IOException e) {            e.printStackTrace();        }    }}

在 Java 7 以及后续版本中,反对 try-with-resources,任何实现 java.lang.AutoCloseable 接口的类,包含 java.io.Closeable 的实现类,都能够通过 try-with-resources 来敞开。

下面代码通过 try-with-resources 能够简化为:

try (InputStream inputStream = new FileInputStream("/my/file")) {    // ...} catch (Exception e) {    e.printStackTrace();}

反对定义多个 resources

通过 JDBC 查询数据库时,会顺次创立 ConnectionStatmentResultSet,并且这三个资源都须要敞开,那么能够这样写:

try (Connection connection = DriverManager.getConnection(url, user, password);     Statement statement = connection.createStatement();     ResultSet resultSet = statement.executeQuery("SELECT ...")) {    // ...} catch (Exception e) {    // ...}

多个 resources 的敞开程序

如果在 try 中定义了多个 resources,那么它们敞开的程序和创立的程序是相同的。下面的例子中,顺次创立了 ConnectionStatmentResultSet 对象,最终敞开时会顺次敞开 ResultSetStatmentConnection,所以不必放心 Connection 会先 close。

官网文档:

Note that the close methods of resources are called in the opposite order of their creation.

catch、finally 和 close 的先后顺序

官网文档:

In a try-with-resources statement, any catch or finally block is run after the resources declared have been closed.

在 try-with-resources 中,catch 和 finally 中的代码在资源敞开之后运行。

以下是一种谬误写法:

Connection connection = DriverManager.getConnection(url, user, password);try (Connection connection2 = connection) {    // ...} catch (SQLException e) {    e.printStackTrace();} finally {    try {        Statement statement = connection.createStatement(); // 异样,此时 Connection 已敞开        // ...    } catch (SQLException ex) {        ex.printStackTrace();    }}

创立和敞开 resources 时的异样解决

在 try-with-resources 中,如果创立资源产生异样,即 try (...) 中小括号里的代码出现异常,以及 close 时产生异样,都是会在 catch 中捕捉到的。例如:

try (InputStream inputStream = new FileInputStream("/not/exist/file")) {    // ...} catch (Exception e) {    e.printStackTrace(); // 如果文件不存在,这里会捕捉异样}

这段代码中,如果 new FileInputStream("/not/exist/file") 对应的文件不存在,就会抛出异样,这个异样会在上面的 catch 中捕捉到。

Suppressed Exceptions

在 try-with-resources 中,如果 try block(即 try 前面大括号中的代码)抛出异样,会触发资源的 close,如果此时 close 也产生了异样,那么 catch 中会捕捉到哪一个呢?

因为 close 抛出异样不是很常见,所以本人实现一个 AutoCloseable 实现类:

public class MyResource implements AutoCloseable {    public void doSomething() throws Exception {        throw new Exception("doSomething exception");    }    @Override    public void close() throws Exception {        throw new Exception("close exception");    }}

测试代码:

public class Test {    public static void main(String[] args) {        try (MyResource myResource = new MyResource()) {            myResource.doSomething();        } catch (Exception e) {            e.printStackTrace();        }    }}

运行后果:

java.lang.Exception: doSomething exception    at com.xxg.MyResource.doSomething(MyResource.java:6)    at com.xxg.Test.main(Test.java:12)    Suppressed: java.lang.Exception: close exception        at com.xxg.MyResource.close(MyResource.java:11)        at com.xxg.Test.main(Test.java:13)

能够看到 catch 中捕捉的是 doSomething() 办法抛出的异样,同时这个异样会蕴含一个 Suppressed Exception,即 close() 办法抛出的异样。

如果想间接拿到 close() 办法抛出的异样,能够通过 Throwable.getSuppressed() 办法获取:

try (MyResource myResource = new MyResource()) {    myResource.doSomething();} catch (Exception e) {    // e 是 doSomething 抛出的异样,想要拿到 close 办法抛出的异样须要通过 e.getSuppressed 办法获取    Throwable[] suppressedExceptions = e.getSuppressed();    Throwable closeException = suppressedExceptions[0];    closeException.printStackTrace();}

Closeable 和 AutoCloseable

AutoCloseable 是 Java 7 新增的接口,Closeable 早就有了。二者的关系是 Closeable extends AutoCloseable。二者都仅蕴含一个 close() 办法。那么为什么 Java 7 还要新增 AutoCloseable 接口呢?

Closeablejava.io 包下,次要用于 IO 相干的资源的敞开,其 close() 办法定义了抛出 IOException 异样。其实现类实现 close() 办法时,不容许抛出除 IOExceptionRuntimeException 外其余类型的异样。

AutoCloseable 位于 java.lang 包下,应用更宽泛。其 close() 办法定义是 void close() throws Exception,也就是它的实现类的 close() 办法对异样抛出是没有限度的。

参考文档

  • https://docs.oracle.com/javas...

关注我