Try with Resources
1. 概述
从Java 7开始,Java支持使用带有资源的try(Try with Resources),允许我们声明要在try块中使用的资源,并保证在该块执行后关闭该资源。
声明的资源必须实现AutoCloseable接口。
2. 使用try-with-resources
简单地说,要自动关闭资源,必须在try中声明和初始化资源,如下所示:
1 try (PrintWriter writer = new PrintWriter(new File("test.txt"))) { 2 writer.println("Hello World"); 3 }
3. 用try-with-resources替换try-catch-finally
使用新的try-with-resources功能的简单而明显的方法是用它来替换传统而冗长try-catch-finally块。
让我们比较以下代码示例–首先是典型的try-catch-finally块,然后是使用等效try-with-resources块的新方法:
1 Scanner scanner = null; 2 try { 3 scanner = new Scanner(new File("test.txt")); 4 while (scanner.hasNext()) { 5 System.out.println(scanner.nextLine()); 6 } 7 } catch (FileNotFoundException e) { 8 e.printStackTrace(); 9 } finally { 10 if (scanner != null) { 11 scanner.close(); 12 } 13 }
下面是使用try-with-resources的超级简洁解决方案:
1 try (Scanner scanner = new Scanner(new File("test.txt"))) { 2 while (scanner.hasNext()) { 3 System.out.println(scanner.nextLine()); 4 } 5 } catch (FileNotFoundException fnfe) { 6 fnfe.printStackTrace(); 7 }
4. 用try-with-resources声明多个资源
多个资源可以在try-with-resources块中声明,用分号将它们分开:
1 try (Scanner scanner = new Scanner(new File("testRead.txt")); 2 PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) { 3 while (scanner.hasNext()) { 4 writer.print(scanner.nextLine()); 5 } 6 }
5. 自定义一个可以自动关闭的资源类
要构造一个能被try-with-resources块正确处理的自定义资源,类应该实现Closeable或AutoCloseable接口,并重写close方法:
1 public class MyResource implements AutoCloseable { 2 @Override 3 public void close() throws Exception { 4 System.out.println("Closed MyResource"); 5 } 6 }
6. 资源关闭顺序
首先被定义或获取的资源将最后关闭;让我们看一个示例:
资源1:
1 public class AutoCloseableResourcesFirst implements AutoCloseable { 2 3 public AutoCloseableResourcesFirst() { 4 System.out.println("Constructor -> AutoCloseableResources_First"); 5 } 6 7 public void doSomething() { 8 System.out.println("Something -> AutoCloseableResources_First"); 9 } 10 11 @Override 12 public void close() throws Exception { 13 System.out.println("Closed AutoCloseableResources_First"); 14 } 15 }
资源2:
1 public class AutoCloseableResourcesSecond implements AutoCloseable { 2 3 public AutoCloseableResourcesSecond() { 4 System.out.println("Constructor -> AutoCloseableResources_Second"); 5 } 6 7 public void doSomething() { 8 System.out.println("Something -> AutoCloseableResources_Second"); 9 } 10 11 @Override 12 public void close() throws Exception { 13 System.out.println("Closed AutoCloseableResources_Second"); 14 } 15 }
运行代码:
1 private void orderOfClosingResources() throws Exception { 2 try (AutoCloseableResourcesFirst af = new AutoCloseableResourcesFirst(); 3 AutoCloseableResourcesSecond as = new AutoCloseableResourcesSecond()) { 4 5 af.doSomething(); 6 as.doSomething(); 7 } 8 }
输出:
1 Constructor -> AutoCloseableResources_First 2 Constructor -> AutoCloseableResources_Second 3 Something -> AutoCloseableResources_First 4 Something -> AutoCloseableResources_Second 5 Closed AutoCloseableResources_Second 6 Closed AutoCloseableResources_First
7. catch & finally
一个 try-with-resources 块仍然可以有 catch 和 finally 块,这与传统 try 块的工作方式相同。
8. Java 9: 有效最终变量(Effectively Final Variables)
在Java 9之前,我们只能在try-with-resources块中使用新变量:
1 try (Scanner scanner = new Scanner(new File("testRead.txt")); 2 PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) { 3 // omitted 4 }
如上所示,当声明多个资源时,这尤其冗长。作为Java 9和JEP 213的一部分,我们现在可以在try-with-resources块中使用final变量和有效最终变量(Effectively Final Variables):
1 final Scanner scanner = new Scanner(new File("testRead.txt")); 2 PrintWriter writer = new PrintWriter(new File("testWrite.txt")) 3 try (scanner;writer) { 4 // omitted 5 }
简单地说,如果变量在第一次赋值之后没有改变,那么它实际上就是final(有效最终变量),即使它没有显式地标记为final。
如上所示,scanner变量显式声明为final,因此我们可以将其与try-with-resources 块一起使用。尽管writer变量不是显式final,但在第一次赋值之后它没有改变。因此,我们也可以使用writer变量。
9. 题外话
在使用锁的时候,不要使用try-with-resources 语句。首先,解锁的方法名不是close。其次,即使将它重命名,try-with-resources 也无法正常工作。try-with-resources 的首部希望声明一个新的变量,但是如果使用一个锁,你可能是想多个线程共享那个变量而不是新的变量。