32.md 7.5 KB
Newer Older
W
wizardforcel 已提交
1
# 异常处理 – `try-with-resources`语句
W
init  
wizardforcel 已提交
2 3 4

> 原文: [https://javabeginnerstutorial.com/core-java-tutorial/exception-handling-try-resources/](https://javabeginnerstutorial.com/core-java-tutorial/exception-handling-try-resources/)

W
wizardforcel 已提交
5
通常,`finally`块用于关闭所有资源(即文件,数据库连接,套接字或在任务完成后应关闭的任何东西),以防止任何泄漏。
W
init  
wizardforcel 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

*示例代码:*

```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
}
```

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

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

W
wizardforcel 已提交
34
因此,使用`try-with-resources`语句的同一示例可以写成:
W
init  
wizardforcel 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47

```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
}
```

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

W
wizardforcel 已提交
50
如您所见,使用`try-with-resources`语句的代码
W
init  
wizardforcel 已提交
51 52 53

*   减少行数即可读取。
*   代码看起来很整洁。
W
wizardforcel 已提交
54
*   如果`finally`块的唯一目的只是关闭资源,则不需要。
W
init  
wizardforcel 已提交
55 56
*   自动资源管理。

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

W
wizardforcel 已提交
59
**值得注意的要点**:
W
init  
wizardforcel 已提交
60

W
wizardforcel 已提交
61
*   `try-with-resources`语句就像普通的`try`语句一样。 它可以拥有常规`catch`和`finally`块。
W
wizardforcel 已提交
62 63
*   重要的是要记住,在`catch`和`finally`块运行之前,已声明的资源已关闭。
*   要在同一`try`语句中声明多个资源,必须使用
W
init  
wizardforcel 已提交
64 65 66 67 68 69 70 71

例如,

```java
try(BufferedReader br = new BufferedReader(new FileReader("C://test.txt")); 
       ZipFile zf = new ZipFile("test1.zip")){…}
```

W
wizardforcel 已提交
72
*   一旦`try-with-resources`块完成(成功完成或引发异常),这些资源的`close`方法将以相反的顺序自动调用。
W
init  
wizardforcel 已提交
73 74 75

考虑以上示例,

W
wizardforcel 已提交
76
`ZipFile`和`BufferedWriter`的`close()`方法将按照上述顺序(即反向顺序)被调用,以避免可能出现的任何依赖问题。
W
init  
wizardforcel 已提交
77 78

*   如果在初始化任何资源时有任何问题,那么到目前为止所有已初始化的资源都将以相反的顺序关闭。
W
wizardforcel 已提交
79 80 81
*   要将自定义类用作资源,这些类必须实现[`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`进行自定义。
W
init  
wizardforcel 已提交
82

W
wizardforcel 已提交
83
## `AutoCloseable`接口的自定义实现:
W
init  
wizardforcel 已提交
84

W
wizardforcel 已提交
85
`AutoCloseable`接口非常容易实现,因为它只有一个方法`close()`。
W
init  
wizardforcel 已提交
86 87 88 89 90 91 92

```java
public interface AutoClosable {
    public void close() throws Exception;
}
```

W
wizardforcel 已提交
93
让我们创建两个实现`AutoCloseable`接口的自定义类,
W
init  
wizardforcel 已提交
94

W
wizardforcel 已提交
95
`Bar.java`
W
init  
wizardforcel 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

```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");
	}
}
```

W
wizardforcel 已提交
112
`Foo.java`
W
init  
wizardforcel 已提交
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129

```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...");
	}
}
```

W
wizardforcel 已提交
130
`Foo`类的`doSomething()`和`close()`方法引发异常。
W
init  
wizardforcel 已提交
131

W
wizardforcel 已提交
132
`MyTryWithResources.java`
W
init  
wizardforcel 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149

```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...");
		}
	}
}
```

W
wizardforcel 已提交
150
### 输出:
W
init  
wizardforcel 已提交
151 152 153

![try with resources output](img/0a4187305e38068412cde02dfaef305e.png)

W
wizardforcel 已提交
154
**说明**:
W
init  
wizardforcel 已提交
155

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

W
wizardforcel 已提交
159
这是在`try-with-resources`中要理解的主要概念。 让我们尝试逐步传播这些信息,
W
init  
wizardforcel 已提交
160

W
wizardforcel 已提交
161 162 163 164 165 166 167 168
*   `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`”显示在控制台上。
W
init  
wizardforcel 已提交
169 170
*   然后,终于执行了块。

W
wizardforcel 已提交
171 172
3.  如果您还想检索抑制的异常怎么办? 不要担心。 在 Java SE 7 和更高版本中,可以使用`getSuppressed()`方法检索它们。
4.  尽管关闭特定资源时会引发异常,但是所有打开的资源都将被关闭,而与引发的异常无关。 在我们的示例中,尽管关闭`Foo`资源时发生异常,但`Bar`资源也成功关闭。
W
init  
wizardforcel 已提交
173

W
wizardforcel 已提交
174
为了获取被抑制的异常,只需将行`exception_handler_reference_variable.getSuppressed()`添加到`MyTryWithResources`类的`catch`块中,如下所示,
W
init  
wizardforcel 已提交
175 176 177 178 179 180 181 182

```java
catch(Exception ex){
			System.out.println("No. of suppressed exceptions: " + ex.getSuppressed().length);
			System.out.println("In catch... " + ex);
		}
```

W
wizardforcel 已提交
183
### 输出:
W
init  
wizardforcel 已提交
184 185 186 187 188 189 190 191 192 193 194 195 196 197

```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...
```

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