Java核心概念(1):Try with Resources

Java核心概念(1):Try with Resources

1. 概述

从Java 7开始,Java支持使用带有资源的try(Try with Resources),允许我们声明要在try块中使用的资源,并保证在该块执行后关闭该资源。

声明的资源必须实现AutoCloseable接口。

2. 使用try-with-resources

简单地说,要自动关闭资源,必须在try中声明和初始化资源,如下所示:

try (PrintWriter writer = new PrintWriter(new File("test.txt"))) {
    writer.println("Hello World");
}

3. 用try-with-resources替换try-catch-finally

使用新的try-with-resources功能的简单而明显的方法是用它来替换传统而冗长try-catch-finally块。

让我们比较以下代码示例–首先是典型的try-catch-finally块,然后是使用等效try-with-resources块的新方法:

Scanner scanner = null;
try {
    scanner = new Scanner(new File("test.txt"));
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} finally {
    if (scanner != null) {
        scanner.close();
    }
}

下面是使用try-with-resources的超级简洁解决方案:

try (Scanner scanner = new Scanner(new File("test.txt"))) {
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException fnfe) {
    fnfe.printStackTrace();
}

在这里您可以进一步探索Scanner类

4. 用try-with-resources声明多个资源

多个资源可以在try-with-resources块中声明,用分号将它们分开:

try (Scanner scanner = new Scanner(new File("testRead.txt"));
    PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
    while (scanner.hasNext()) {
	writer.print(scanner.nextLine());
    }
}

5. 自定义一个可以自动关闭的资源类

要构造一个能被try-with-resources块正确处理的自定义资源,类应该实现CloseableAutoCloseable接口,并重写close方法:

public class MyResource implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("Closed MyResource");
    }
}

6. 资源关闭顺序

首先被定义或获取的资源将最后关闭;让我们看一个示例:

资源1:

public class AutoCloseableResourcesFirst implements AutoCloseable {
 
    public AutoCloseableResourcesFirst() {
        System.out.println("Constructor -> AutoCloseableResources_First");
    }
 
    public void doSomething() {
        System.out.println("Something -> AutoCloseableResources_First");
    }
 
    @Override
    public void close() throws Exception {
        System.out.println("Closed AutoCloseableResources_First");
    }
}

资源2:

public class AutoCloseableResourcesSecond implements AutoCloseable {
 
    public AutoCloseableResourcesSecond() {
        System.out.println("Constructor -> AutoCloseableResources_Second");
    }
 
    public void doSomething() {
        System.out.println("Something -> AutoCloseableResources_Second");
    }
 
    @Override
    public void close() throws Exception {
        System.out.println("Closed AutoCloseableResources_Second");
    }
}

运行代码:

private void orderOfClosingResources() throws Exception {
    try (AutoCloseableResourcesFirst af = new AutoCloseableResourcesFirst();
        AutoCloseableResourcesSecond as = new AutoCloseableResourcesSecond()) {
 
        af.doSomething();
        as.doSomething();
    }
}

输出:

Constructor -> AutoCloseableResources_First
Constructor -> AutoCloseableResources_Second
Something -> AutoCloseableResources_First
Something -> AutoCloseableResources_Second
Closed AutoCloseableResources_Second
Closed AutoCloseableResources_First

7. catch & finally

一个try-with-resources块仍然可以有catchfinally块,这与传统try块的工作方式相同。

8. Java 9: 有效最终变量(Effectively Final Variables)

在Java 9之前,我们只能在try-with-resources块中使用新变量:

try (Scanner scanner = new Scanner(new File("testRead.txt")); 
    PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) { 
    // omitted
}

如上所示,当声明多个资源时,这尤其冗长。作为Java 9和JEP 213的一部分,我们现在可以在try-with-resources块中使用final变量和有效最终变量(Effectively Final Variables)

final Scanner scanner = new Scanner(new File("testRead.txt"));
PrintWriter writer = new PrintWriter(new File("testWrite.txt"))
try (scanner;writer) { 
    // omitted
}

简单地说,如果变量在第一次赋值之后没有改变,那么它实际上就是final(有效最终变量),即使它没有显式地标记为final

如上所示,scanner变量显式声明为final,因此我们可以将其与try-with-resources块一起使用。尽管writer变量不是显式final,但在第一次赋值之后它没有改变。因此,我们也可以使用writer变量。

9. 总结

在这篇文章中,我们探讨了如何使用try-with-resources,如何用try-with-resources来替换trycatchfinally,如何构建一个实现AutoCloseable的自定义资源类以及资源被关闭的顺序。

所有例子的完整源代码在这个Github项目中。

References:

编辑于 2020-09-28 16:19