Java 7和Java 8之间的细微自动关闭合同更改

Java 7的try-with-resources语句和与该语句一起使用的AutoCloseable类型的一个不错的功能是,静态代码分析工具可以检测到资源泄漏。 例如,Eclipse:

资源泄漏

具有以上配置并尝试运行以下程序时,您将收到三个警告:

public static void main(String[] args) 
throws Exception {
    Connection c = DriverManager.getConnection(
         "jdbc:h2:~/test", "sa", "");
    Statement s = c.createStatement();
    ResultSet r = s.executeQuery("SELECT 1 + 1");
    r.next();
    System.out.println(r.getInt(1));
}

输出是琐碎的

2

警告会在所有csr上发出。 一种快速的解决方案(不要这样做!)是使用Eclipse特定的SuppressWarnings参数抑制警告:

@SuppressWarnings("resource")
public static void main(String[] args) 
throws Exception {
    ...
}

毕竟,WeKnowWhatWeReDoing™,这只是一个简单的示例,对吧?

错误!

即使对于简单的示例(至少在Java 7之后),解决此问题的正确方法是使用轻松的try-with-resources语句。

public static void main(String[] args) 
throws Exception {
    try (Connection c = DriverManager.getConnection(
             "jdbc:h2:~/test", "sa", "");
         Statement s = c.createStatement();
         ResultSet r = s.executeQuery("SELECT 1 + 1")) {

        r.next();
        System.out.println(r.getInt(1));
    }
}

实际上,如果Eclipse可以自动修复此警告并将所有单独的语句包装在try-with-resources语句中,那就太好了。 请支持此功能请求!

Java 8处理了什么?

在Java 8中, AutoCloseable上的合同已非常微妙地更改(或直率地更改了,具体取决于您的观点)。

Java 7版本

在不再需要时必须关闭的资源。

注意单词"must"

Java 8版本

在关闭之前可以保存资源(例如文件或套接字句柄)的对象。 退出在资源规范头中已声明该对象的try-with-resources块时,将自动调用AutoCloseable对象的close()方法。 这种构造可确保及时释放,避免资源耗尽异常和可能发生的错误。

API注意:

即使并非所有子类或实例都拥有可释放的资源,基类也有可能并且实际上是常见的。 对于必须完全通用的代码,或者已知AutoCloseable实例需要释放资源的代码,建议使用try-with-resources构造。 但是,当使用诸如Stream的功能同时支持基于I / O和基于非I / O的形式时,使用非基于I / O的形式时通常不需要使用资源尝试模块。

简而言之,从Java 8开始, AutoCloseable更具暗示性,表明您可能正在使用需要关闭的资源,但这并非一定如此。

这类似于Iterable契约,后者没有说明您只能对Iterable进行一次还是多次迭代,但是它强加了foreach循环所需的契约。

我们什么时候拥有“可选的可关闭”资源?

jOOQ为例。 与JDBC不同,jOOQ 查询在jOOQ 3.7中被设置为AutoCloseable )可能表示资源,也可能不表示资源,这取决于您如何执行。 默认情况下,它不是资源:

try (Connection c = DriverManager.getConnection(
        "jdbc:h2:~/test", "sa", "")) {

    // No new resources created here:
    ResultQuery<Record> query =
        DSL.using(c).resultQuery("SELECT 1 + 1");

    // Resources created and closed immediately
    System.out.println(query.fetch());
}

输出再次是:

+----+
|   2|
+----+
|   2|
+----+

但是现在,我们再次在query变量上出现了Eclipse警告,说有一个资源需要关闭,即使通过这种方式使用jOOQ,我们知道事实并非如此。 上面的代码中唯一的资源是JDBC Connection ,并且已正确处理。 jOOQ内部的jOOQ PreparedStatementResultSet已完全处理,并急切地关闭了。

然后,为什么要首先实现AutoCloseable?

jOOQ与JDBC的默认行为相反。

  • 在JDBC中,默认情况下所有工作都是延迟进行的,并且必须显式关闭资源。
  • 在jOOQ中,默认情况下会急切地完成所有工作,并且可以有选择地使资源保持活动状态。

例如,以下代码将保持打开的PreparedStatementResultSet

try (Connection c = DriverManager.getConnection(
        "jdbc:h2:~/test", "sa", "");

     // We "keep" the statement open in the ResultQuery
     ResultQuery<Record> query =
         DSL.using(c)
            .resultQuery("SELECT 1 + 1")
            .keepStatement(true)) {

    // We keep the ResultSet open in the Cursor
    try (Cursor<Record> cursor = query.fetchLazy()) {
        System.out.println(cursor.fetchOne());
    }
}

在此版本中,我们在Eclipse中不再有任何警告,但是上述版本实际上是使用jOOQ API时的例外。

Java 8的Stream API也是如此。 有趣的是,Eclipse在这里不发出任何警告:

Stream<Integer> stream = Arrays.asList(1, 2, 3).stream();
stream.forEach(System.out::println);

结论

首先,资源泄漏检测似乎是一个不错的IDE /编译器功能。 但是,避免误报很难。 具体而言,因为Java 8改变了合同AutoCloseable ,实现者被允许执行AutoCloseable为一种方便的契约,而不是作为一种资源存在,必须关闭的清晰指示符。

这使IDE很难(甚至不是不可能)检测第三方合同(非JDK API)的资源泄漏,而这些合同通常并不为人所知。 与静态代码分析工具一样,该解决方案通常只是关闭潜在的资源泄漏检测:

资源泄漏解决方案

翻译自: https://www.javacodegeeks.com/2015/12/subtle-autocloseable-contract-change-java-7-java-8.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值