Java try-with-resources与示例

时间:2020-01-09 10:34:58  来源:igfitidea点击:

Java 7及更高版本的新功能try-with-resources可用于自动资源管理。通过使用Java中的try-with-resources功能,可以使用try语句本身声明一个或者多个资源。 try-with-resources语句可确保声明的资源最后自动关闭。

资源是一个对象,程序完成后必须将其关闭。例如,打开的文件流,数据库连接等。

在try-with-resources之前

在使用Java 7 try-with-resources来关闭资源之前,我们必须做两件事

  • 显式调用close()方法以关闭打开的资源。

  • 在finally块中调用close()方法以确保关闭资源,而不管try语句是正常完成还是突然完成。

带有final的示例代码

public class FinallyDemo {
  public static void main(String[] args) {
    BufferedReader br = null;
    try {
      br = new BufferedReader(new FileReader("D:\test.txt"));
      System.out.println(br.readLine());
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        if (br != null){
         System.out.println("Closing the file");
         br.close();
        }
          
      } catch (IOException ex) {
        ex.printStackTrace();
      }
    }
  }
}

请注意此处的痛点

  • 我们正在编写一整段代码来关闭资源,否则将导致资源保持打开状态,从而导致性能降低和内存泄漏。

  • 关闭资源时,我们不得不再次使用try-catch块。

在Java中使用try-with-resources

try-with-resources提供了使用try语句声明资源的选项。通过try-with-resources确保关闭资源,而不管try语句是正常完成还是突然完成。

资源尝试Java示例代码

public class FinallyDemo {
  public static void main(String[] args) {	
    try (BufferedReader br = new BufferedReader(new FileReader("D:\test.txt"))){
      System.out.println(br.readLine());
    } catch (IOException e) {
      e.printStackTrace();
    } 
  }
}

现在,资源在try关键字之后的括号内声明,最后不需要块。 BufferedReader对象现在将自动关闭。我们可以看到由于使用资源而减少了多少样板代码。

AutoCloseable接口

现在的问题是如何使用Java中的try-with-resources自动关闭资源。这是因为Java 7中引入了接口java.lang.AutoCloseable。任何实现java.lang.AutoCloseable接口的对象都可以用作带try-with-resource的资源。如果与try-with-resource一起使用的资源未实现AutoCloseable接口,则会导致编译时错误。

Java示例

public class TryResource {
  public static void main(String[] args) {
    try (MyAutoCloseResource myResource = new MyAutoCloseResource()) {
      System.out.println("MyAutoCloseResource created in try-with-resources");
    }catch(Exception ex){
      ex.printStackTrace();
    }
  }
	
  // Class implementing AutoCoseable
  static class MyAutoCloseResource implements AutoCloseable { 
    @Override
    public void close() throws Exception {
      System.out.println("Closing MyAutoCloseResource");
    }
  }
}

输出:

MyAutoCloseResource created in try-with-resources
Closing MyAutoCloseResource

我们可以看到资源的close()方法是自动调用的。

Java 9中try-with-resources 增强

如果我们看到上面给出的使用try-with-resources的示例,那么要注意的一件事是,必须自动关闭的资源是在try-with-resources的try语句中创建的,它是使用时的限制之一Java 9之前的try-with-resources。

try (BufferedReader br = new BufferedReader(new FileReader("D:\test.txt"))){
    System.out.println(br.readLine());
}

即使已经声明了资源,也必须在try-with-resources中对其进行重新引用,以使其自动关闭。

public class TryResources {
  public static void main(String[] args) throws IOException{    
    BufferedReader br = new BufferedReader(new FileReader("D:\test.txt"));
    try (BufferedReader refbr = br){
      System.out.println(refbr.readLine());
    } catch (IOException e) {
      e.printStackTrace();
    } 
  }
}

从Java 9开始,此限制不再存在,现在我们可以在外部声明资源,并在try语句中使用相同的引用。唯一的限制是try-with-resource构造中带有的引用变量必须是final或者有效的final。

public class TryResources {
  public static void main(String[] args) throws IOException{    
    BufferedReader br = new BufferedReader(new FileReader("D:\test.txt"));
    // same reference used 
    try (br){
      System.out.println(br.readLine());
    } catch (IOException e) {
      e.printStackTrace();
    } 
  }
}

在try-with-resources语句中声明多个资源

我们可以使用try-with-resource语句声明多个资源,资源之间用分号分隔。
例如

try (ZipFile zf = new ZipFile(zipFileName); BufferedWriter writer = Files.newBufferedWriter(outputFilePath, charset))

资源以其创建相反的顺序关闭,因此将首先调用BufferedWriter的close()方法,然后再调用ZipFile的close()方法。

使用try-with-resource抑制异常

如果从try块引发异常,并且从try-with-resource语句引发异常,那么抑制try-with-resource语句引发的异常,并返回try块引发的异常。

这与在finally块的情况下发生的情况形成直接对比。如果两个块都抛出异常,则返回由finally块抛出的异常,并抑制try块抛出的异常。

让我们尝试通过一个示例来清除它。

首先,我们将看到带有finally块的代码,其中try和finally块均抛出异常。在代码中,还有一个AutoCloseable接口的自定义实现MyAutoCloseResource。我们可以看到close()方法显式抛出了异常。从try块也引发异常。

public class TryResource {
  public static void main(String[] args) {
    TryResource tr = new TryResource();
    try {
      tr.Test();
    } catch (Exception e) {
      System.out.println("Exception caught -- " + e.getMessage());
    }
  }
	
  private void Test() throws Exception{
    MyAutoCloseResource myResource = null;
    try {
      myResource  = new MyAutoCloseResource();
      throw new Exception("Exception in class Test");
       
    }finally{
      if(myResource != null){
      myResource.close();			
      }
    }
  }
  // Class implementing AutoCoseable
  class MyAutoCloseResource implements AutoCloseable {	  
    @Override
    public void close() throws Exception {
      System.out.println("Closing MyAutoCloseResource");
      throw new Exception("Exception while closing resource");
    }
  }
}

输出:

Closing MyAutoCloseResource
Exception caught -- Exception while closing resource

从输出中可以看到,在finally块中引发的异常是返回的异常。

现在,让我们看一看带有try-with-resource语句的示例。

public class TryResource {
  public static void main(String[] args) {
    TryResource tr = new TryResource();
    try {
      tr.Test();
    } catch (Exception e) {
      System.out.println("Exception caught -- " + e.getMessage());
      System.out.println("Suppressed Exception  -- " + e.getSuppressed()[0]);
    }
  }
	
  private void Test() throws Exception{
    try(MyAutoCloseResource myResource = new MyAutoCloseResource()) {		
      throw new Exception("Exception in class Test");         
    }
  }
  // Class implementing AutoCoseable
  class MyAutoCloseResource implements AutoCloseable {
    @Override
    public void close() throws Exception {
      System.out.println("Closing MyAutoCloseResource");
      throw new Exception("Exception while closing resource");
    }
  }
}

输出:

Closing MyAutoCloseResource
Exception caught -- Exception in class Test
Suppressed Exception -- java.lang.Exception: Exception while closing resource

从输出中可以看到,在try块中引发的异常是返回的异常。
要检索这些抑制的异常,可以从try块引发的异常中调用Throwable.getSuppressed方法,如Java代码所示。