💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 异常处理 – `try-with-resources`语句 > 原文: [https://javabeginnerstutorial.com/core-java-tutorial/exception-handling-try-resources/](https://javabeginnerstutorial.com/core-java-tutorial/exception-handling-try-resources/) 通常,`finally`块用于关闭所有资源(即文件,数据库连接,套接字或在任务完成后应关闭的任何东西),以防止任何泄漏。 *示例代码:* ```java public class ResourceMgt { public static void main(String[] args) { BufferedReader br = null; try { br = new BufferedReader(new FileReader("C://test.txt")); System.out.println(br.readLine()); } catch (IOException e) { e.printStackTrace(); } finally { if (br != null) try { br.close(); } catch (IOException e) { e.printStackTrace(); } } //finally } // main } ``` 如果您看上面的示例代码,要关闭`BufferedReader`资源,我们必须检查它是否仍然打开,然后调用`close()`方法。 `close`方法可能会引发异常,因此必须将其包围在`try catch`块中。 对于每个打开的资源,都将重复此代码。 对于大型应用,由于这个原因,您将看到很多重复的代码。 在 Java 7 和更高版本中,`try-with-resources`语句可确保在该语句的末尾关闭所有打开的资源。 因此,`try-with-resources`语句不过是声明一个或多个资源的`try`语句。 所谓资源就是实现`java.lang.AutoCloseable`接口的任何对象。 该接口又包括实现`java.io.Closeable`接口的所有对象。 因此,使用`try-with-resources`语句的同一示例可以写成: ```java public class ResourceMgt { public static void main(String[] args) { try(BufferedReader br = new BufferedReader(new FileReader("C://test.txt"))){ System.out.println(br.readLine()); } catch (IOException e) { e.printStackTrace(); } } // main } ``` 在`try`关键字之后立即引入一个括号,并且所有资源都应仅在该括号内声明。 这些资源用分号分隔。 如您所见,使用`try-with-resources`语句的代码 * 减少行数即可读取。 * 代码看起来很整洁。 * 如果`finally`块的唯一目的只是关闭资源,则不需要。 * 自动资源管理。 在我们的示例中,由于在`try-with-resource`语句中声明了`BufferedReader`实例,因此无论`try`语句是否成功完成,都将关闭它(如``readLine()`方法可以引发`IOException`)。 **值得注意的要点**: * `try-with-resources`语句就像普通的`try`语句一样。 它可以拥有常规`catch`和`finally`块。 * 重要的是要记住,在`catch`和`finally`块运行之前,已声明的资源已关闭。 * 要在同一`try`语句中声明多个资源,必须使用 例如, ```java try(BufferedReader br = new BufferedReader(new FileReader("C://test.txt")); ZipFile zf = new ZipFile("test1.zip")){…} ``` * 一旦`try-with-resources`块完成(成功完成或引发异常),这些资源的`close`方法将以相反的顺序自动调用。 考虑以上示例, `ZipFile`和`BufferedWriter`的`close()`方法将按照上述顺序(即反向顺序)被调用,以避免可能出现的任何依赖问题。 * 如果在初始化任何资源时有任何问题,那么到目前为止所有已初始化的资源都将以相反的顺序关闭。 * 要将自定义类用作资源,这些类必须实现[`lang.AutoCloseable`](https://docs.oracle.com/javase/8/docs/api/java/lang/AutoCloseable.html)或[`java.io.Closeable`](https://docs.oracle.com/javase/8/docs/api/java/io/Closeable.html)接口。 * `Closeable`和`AutoCloseable`接口的`close()`方法分别引发`IOException`和`Exception`类型的异常。 * 由于`AutoCloseable`接口的子类可以覆盖`close`方法的此行为以引发专门的异常(例如`IOException`或根本没有异常),因此最好使用`AutoCloseable`进行自定义。 ## `AutoCloseable`接口的自定义实现: `AutoCloseable`接口非常容易实现,因为它只有一个方法`close()`。 ```java public interface AutoClosable { public void close() throws Exception; } ``` 让我们创建两个实现`AutoCloseable`接口的自定义类, `Bar.java` ```java public class Bar implements AutoCloseable{ public Bar(){ System.out.println("Inside Bar Class"); } public void doSomething(){ System.out.println("Doing something in Bar!"); } public void close() throws Exception{ System.out.println("Closed Bar"); } } ``` `Foo.java` ```java public class Foo implements AutoCloseable{ public Foo(){ System.out.println("Inside Foo Class"); } public void doSomething() throws Exception{ throw new Exception("Exception from Foo doSomething() method"); } public void close() throws Exception{ System.out.println("Closing Foo"); throw new Exception("Unable to close Foo..."); } } ``` `Foo`类的`doSomething()`和`close()`方法引发异常。 `MyTryWithResources.java` ```java public class MyTryWithResources { public static void main(String[] args){ try(Bar b = new Bar(); Foo f = new Foo()){ b.doSomething(); f.doSomething(); }catch(Exception ex){ System.out.println("In catch... " + ex); }finally{ System.out.println("In finally..."); } } } ``` ### 输出: ![try with resources output](https://img.kancloud.cn/e9/c6/e9c6049f02cfd025d913f20aac1b93df_686x191.png) **说明**: 1. 这里首先要注意的是打开和关闭资源的顺序。 打开栏,然后打开`Foo`,但在关闭时,遵循相反的顺序。 `Foo`关闭,然后关闭`Bar`资源。 2. `Foo`的`close()`会引发异常,但是如果您注意到生成的输出,则会将其抑制。 `MyTryWithResources`的`main`方法仅引发由`doSomething()`在`try`块中生成的异常。 这是在`try-with-resources`中要理解的主要概念。 让我们尝试逐步传播这些信息, * `Bar`和`Foo`资源在`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`块执行。 3. 如果您还想检索抑制的异常怎么办? 不要担心。 在 Java SE 7 和更高版本中,可以使用`getSuppressed()`方法检索它们。 4. 尽管关闭特定资源时会引发异常,但是所有打开的资源都将被关闭,而与引发的异常无关。 在我们的示例中,尽管关闭`Foo`资源时发生异常,但`Bar`资源也成功关闭。 为了获取被抑制的异常,只需将行`exception_handler_reference_variable.getSuppressed()`添加到`MyTryWithResources`类的`catch`块中,如下所示, ```java catch(Exception ex){ System.out.println("No. of suppressed exceptions: " + ex.getSuppressed().length); System.out.println("In catch... " + ex); } ``` ### 输出: ```java Inside Bar Class Inside Foo Class Doing something in Bar! Closing Foo Closed Bar No. of suppressed exceptions: 1 In catch... java.lang.Exception: Exception from Foo doSomething() method In finally... ``` 通过遵循这些示例,您可以毫不费力地围绕这个概念进行思考。 祝你今天愉快!