Java try-with-resources简介与其Scala workaround

探讨了Java中资源管理的重要性,介绍了try-with-resources语法糖的使用,简化资源关闭过程,避免资源泄露。同时,对比了C++和C#的资源管理方式,展示了如何在Scala中实现类似功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在我们使用Java编写业务逻辑时,如果打开了一些由外部组件管理的资源(如文件、文件I/O流、数据库连接、网络连接等等),就必须在使用完这些资源之后,通过资源句柄手动关闭。如果不关闭的话,JVM并不会回收它们,就会出现文件被占用无法打开、数据库连接池耗尽等情况。以FileInputStream为例,传统的try-catch-finally写法如下:

public class ResourceCloseSample {
    public static void main(String[] args) {
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream("/home/lmagic/1.txt");
            System.out.println(fileInputStream.read());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

看起来真的是过于累赘了。因此从JDK 7开始,提供了一块语法糖,叫做try-with-resources。简化之后,可以写成这样:

public class ResourceCloseSample {
    public static void main(String[] args) {
        try (FileInputStream fileInputStream = new FileInputStream("/home/lmagic/1.txt")) {
             System.out.println(fileInputStream.read());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

使用try-with-resources的前提是资源句柄(比如上面的FileInputStream对象实例)实现了AutoCloseable接口,我们更熟悉的Closeable接口也是派生自AutoCloseable。这样写可以在资源句柄的作用域结束时自动调用其close()方法,并且仍然支持传统的catch和finally语法,简单方便。

上面使用了try-with-resources的代码反编译之后如下图所示,仍然是try-catch-finally结构,印证了它仅仅是个语法糖。

195230-0bf95168b8d3d843.png

需要注意的是,一旦try-catch-finally结构中的try语句块与finally语句块都抛出了异常,那么后者在异常传递时会覆盖(抑制)掉前者,前者的异常就消失了。因此,JDK 7也为异常的根Throwable增加了addSuppressed()方法,通过该方法能够将两个异常都记录下来,在使用try-with-resources时也不必单独处理。为了说明它,可以自定义一个只会抛出异常的资源:

public class ResourceCloseSample {
    public static void main(String[] args) {
        try (MyResourceHandle myResourceHandle = new MyResourceHandle()) {
             myResourceHandle.open();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class MyResourceHandle implements AutoCloseable {
    public void open() throws Exception {
        throw new Exception("open() method throws exception");
    }

    @Override
    public void close() throws Exception {
        throw new Exception("close() method throws exception");
    }
}

结果如下。

195230-a302834f1fc1293d.png

try-with-resources也能同时使用多个资源,在try后面用分号分隔即可。在使用完后,会先关闭后声明的句柄,后关闭先声明的句柄。以调用HBase的Scan API为例,代码如下:

public class ResourceCloseSample {
    public static void main(String[] args) {
        // 因为创建HBase连接太贵了,所以做成单例
        Connection connection = HBaseConnection.get();
        Scan scan = new Scan().setStartRow(Bytes.toBytes("1")).setStopRow(Bytes.toBytes("7"));

        try (
            Table table = connection.getTable(TableName.valueOf("test_table"));
            ResultScanner scanner = table.getScanner(scan)
        ) {
            for (Result result : scanner) {
                // 处理结果
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

实际上,try-with-resources在Java中出现的已经非常晚了。C++和C#原生就支持在对象生命周期结束时释放资源的方法,前者可以在析构函数中定义,后者可以通过using关键字、IDisposable接口和Dispose()方法实现。但晚出现总比没有好,通过try-with-resources确实能够节省很多不必要的模式性编码,读起来也更简洁。

不过,对于大数据工作者而言,Scala似乎在平时工作中比Java用得多一些(反正我是这样的)。Scala中并不存在try-with-resources语法糖,但这也不妨碍我们自己实现一个。利用泛型、柯里化和高阶函数就可以写出如下方法:

  def tryWithResource[T <: AutoCloseable](handle: T)(func: T => Any): Any = {
    try {
      func(handle)
    } finally {
      if (handle != null) {
        handle.close()
      }
    }
  }

其中,泛型T表示任何继承自AutoCloseable接口的类型,handle表示对应类型的句柄,func是一个函数,代表由句柄进行的操作。然后就可以这样写了:

  def main(args: Array[String]): Unit = {
    tryWithResource(connection.getTable(TableName.valueOf("test_table")) {
      table: Table => {
        tryWithResource(table.getScanner(scan)) {
          scanner => {
            for (result <- scanner) {
              // 处理结果
            }
          }
        }
      }
    })
  }

虽然不及Java的风格来得简便,但大多数情况下都只需要操作一个资源句柄,并且不用处理close()方法的异常,所以还是比较好用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值