Java try-with-resources与示例
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代码所示。