异常处理 – try-with-resources语句

原文: https://javabeginnerstutorial.com/core-java-tutorial/exception-handling-try-resources/

通常,finally块用于关闭所有资源(即文件,数据库连接,套接字或在任务完成后应关闭的任何东西),以防止任何泄漏。

示例代码:

  1. public class ResourceMgt {
  2. public static void main(String[] args) {
  3. BufferedReader br = null;
  4. try {
  5. br = new BufferedReader(new FileReader("C://test.txt"));
  6. System.out.println(br.readLine());
  7. } catch (IOException e) {
  8. e.printStackTrace();
  9. } finally {
  10. if (br != null)
  11. try {
  12. br.close();
  13. } catch (IOException e) {
  14. e.printStackTrace();
  15. }
  16. } //finally
  17. } // main
  18. }

如果您看上面的示例代码,要关闭BufferedReader资源,我们必须检查它是否仍然打开,然后调用close()方法。 close方法可能会引发异常,因此必须将其包围在try catch块中。 对于每个打开的资源,都将重复此代码。 对于大型应用,由于这个原因,您将看到很多重复的代码。

在 Java 7 和更高版本中,try-with-resources语句可确保在该语句的末尾关闭所有打开的资源。 因此,try-with-resources语句不过是声明一个或多个资源的try语句。 所谓资源就是实现java.lang.AutoCloseable接口的任何对象。 该接口又包括实现java.io.Closeable接口的所有对象。

因此,使用try-with-resources语句的同一示例可以写成:

  1. public class ResourceMgt {
  2. public static void main(String[] args) {
  3. try(BufferedReader br = new BufferedReader(new FileReader("C://test.txt"))){
  4. System.out.println(br.readLine());
  5. } catch (IOException e) {
  6. e.printStackTrace();
  7. }
  8. } // main
  9. }

try关键字之后立即引入一个括号,并且所有资源都应仅在该括号内声明。 这些资源用分号分隔。

如您所见,使用try-with-resources语句的代码

  • 减少行数即可读取。
  • 代码看起来很整洁。
  • 如果finally块的唯一目的只是关闭资源,则不需要。
  • 自动资源管理。

在我们的示例中,由于在try-with-resource语句中声明了BufferedReader实例,因此无论try语句是否成功完成,都将关闭它(如``readLine()方法可以引发IOException`)。

值得注意的要点

  • try-with-resources语句就像普通的try语句一样。 它可以拥有常规catchfinally块。
  • 重要的是要记住,在catchfinally块运行之前,已声明的资源已关闭。
  • 要在同一try语句中声明多个资源,必须使用

例如,

  1. try(BufferedReader br = new BufferedReader(new FileReader("C://test.txt"));
  2. ZipFile zf = new ZipFile("test1.zip")){…}
  • 一旦try-with-resources块完成(成功完成或引发异常),这些资源的close方法将以相反的顺序自动调用。

考虑以上示例,

ZipFileBufferedWriterclose()方法将按照上述顺序(即反向顺序)被调用,以避免可能出现的任何依赖问题。

  • 如果在初始化任何资源时有任何问题,那么到目前为止所有已初始化的资源都将以相反的顺序关闭。
  • 要将自定义类用作资源,这些类必须实现lang.AutoCloseablejava.io.Closeable接口。
  • CloseableAutoCloseable接口的close()方法分别引发IOExceptionException类型的异常。
  • 由于AutoCloseable接口的子类可以覆盖close方法的此行为以引发专门的异常(例如IOException或根本没有异常),因此最好使用AutoCloseable进行自定义。

AutoCloseable接口的自定义实现:

AutoCloseable接口非常容易实现,因为它只有一个方法close()

  1. public interface AutoClosable {
  2. public void close() throws Exception;
  3. }

让我们创建两个实现AutoCloseable接口的自定义类,

Bar.java

  1. public class Bar implements AutoCloseable{
  2. public Bar(){
  3. System.out.println("Inside Bar Class");
  4. }
  5. public void doSomething(){
  6. System.out.println("Doing something in Bar!");
  7. }
  8. public void close() throws Exception{
  9. System.out.println("Closed Bar");
  10. }
  11. }

Foo.java

  1. public class Foo implements AutoCloseable{
  2. public Foo(){
  3. System.out.println("Inside Foo Class");
  4. }
  5. public void doSomething() throws Exception{
  6. throw new Exception("Exception from Foo doSomething() method");
  7. }
  8. public void close() throws Exception{
  9. System.out.println("Closing Foo");
  10. throw new Exception("Unable to close Foo...");
  11. }
  12. }

Foo类的doSomething()close()方法引发异常。

MyTryWithResources.java

  1. public class MyTryWithResources {
  2. public static void main(String[] args){
  3. try(Bar b = new Bar(); Foo f = new Foo()){
  4. b.doSomething();
  5. f.doSomething();
  6. }catch(Exception ex){
  7. System.out.println("In catch... " + ex);
  8. }finally{
  9. System.out.println("In finally...");
  10. }
  11. }
  12. }

输出:

try with resources output

说明

  1. 这里首先要注意的是打开和关闭资源的顺序。 打开栏,然后打开Foo,但在关闭时,遵循相反的顺序。 Foo关闭,然后关闭Bar资源。
  2. Fooclose()会引发异常,但是如果您注意到生成的输出,则会将其抑制。 MyTryWithResourcesmain方法仅引发由doSomething()try块中生成的异常。

这是在try-with-resources中要理解的主要概念。 让我们尝试逐步传播这些信息,

  • BarFoo资源在try-with-resources块中创建。
  • Try块开始执行。
  • Bar类的doSomething()成功执行,并输出消息“在Bar中做某事!” 进行控制台。
  • Foo类的doSomething()方法引发异常。
  • 在将控件转移到catch方法以处理异常之前,通过调用资源各自的close()方法来关闭资源。
  • Foo类的close()将消息“正在关闭Foo”打印到控制台,并抛出一个异常,该异常在try块抛出的异常暴露时被抑制。
  • 仅当try块和try-with-resources语句(close()方法)都抛出异常时,try-with-resources语句对这种异常的抑制才发生。
  • Bar类的close()运行,并将消息“Closing Bar”显示在控制台上。
  • 然后,finally块执行。
  1. 如果您还想检索抑制的异常怎么办? 不要担心。 在 Java SE 7 和更高版本中,可以使用getSuppressed()方法检索它们。
  2. 尽管关闭特定资源时会引发异常,但是所有打开的资源都将被关闭,而与引发的异常无关。 在我们的示例中,尽管关闭Foo资源时发生异常,但Bar资源也成功关闭。

为了获取被抑制的异常,只需将行exception_handler_reference_variable.getSuppressed()添加到MyTryWithResources类的catch块中,如下所示,

  1. catch(Exception ex){
  2. System.out.println("No. of suppressed exceptions: " + ex.getSuppressed().length);
  3. System.out.println("In catch... " + ex);
  4. }

输出:

  1. Inside Bar Class
  2. Inside Foo Class
  3. Doing something in Bar!
  4. Closing Foo
  5. Closed Bar
  6. No. of suppressed exceptions: 1
  7. In catch... java.lang.Exception: Exception from Foo doSomething() method
  8. In finally...

通过遵循这些示例,您可以毫不费力地围绕这个概念进行思考。 祝你今天愉快!