Skip to content

Commit deb1abf

Browse files
author
waylau
committed
update
1 parent f3a0f4c commit deb1abf

File tree

8 files changed

+408
-5
lines changed

8 files changed

+408
-5
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Essential Java, is a book about the Essentials of Java Programming.
77
There is also a GitBook version of this book: <http://waylau.gitbooks.io/essential-java>.
88

99

10-
《Java 编程要点》是一本 Java 的开源学习教程,主要介绍 Java 中应用广泛的部分(言外之意,本书不涉 Applet 以及 GUI 框架)。本书也会包括最新版本 Java 8 中的新特性,图文并茂,并通过大量实例让你走近 Java 的世界!
10+
《Java 编程要点》是一本 Java 的开源学习教程,主要介绍 Java 中应用广泛的部分(言外之意,本书不涉 Applet 以及 GUI 框架)。本书包括最新版本 Java 8 中的新特性,以及部分 JDK 9里面的内容,图文并茂,并通过大量实例带你走近 Java 的世界!
1111

1212
本书业余时间所著,水平有限、时间紧张,难免疏漏,欢迎指正,
1313

SUMMARY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
* [I/O 模型的演进](docs/io-model.md)
3131
* [JDBC](docs/jdbc.md)
3232
* [异常](docs/exceptions.md)
33-
* [捕获、处理异常](docs/exceptions-catch-and-handle.md)
33+
* [异常捕获与处理](docs/exceptions-catch-and-handle.md)
34+
* [通过方法声明异常抛出](docs/exceptions-specify-exceptions-thrown.md)
3435
* [如何抛出异常](docs/exceptions-throw.md)
3536
* [使用 try-with-resources 声明](docs/exceptions-try-with-resources.md)
3637
* [未检查异常](docs/exceptions-unchecked-exception.md)
Lines changed: 282 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,283 @@
1-
# 捕获、处理异常
1+
# 异常捕获与处理
2+
3+
本节介绍如何使用三个异常处理程序组件(try、catch 和 finally)来编写异常处理程序。 然后,介绍了 Java SE 7中引入的 try-with-resources 语句。 try-with-resources 语句特别适合于使用`Closeable`的资源(例如流)的情况。
4+
5+
本节的最后一部分将通过一个示例来分析在各种情况下发生的情况。
6+
7+
以下示例定义并实现了一个名为ListOfNumbers的类。 构造时,ListOfNumbers 创建一个ArrayList,其中包含10个序列值为0到9的整数元素。ListOfNumbers类还定义了一个名为writeList的方法,该方法将数列表写入一个名为`OutFile.txt`的文本文件中。 此示例使用在`java.io`中定义的输出类,这些类包含在基本I/O中。
8+
9+
```java
10+
// Note: This class will not compile yet.
11+
import java.io.*;
12+
import java.util.List;
13+
import java.util.ArrayList;
14+
15+
public class ListOfNumbers {
16+
17+
private List<Integer> list;
18+
private static final int SIZE = 10;
19+
20+
public ListOfNumbers () {
21+
list = new ArrayList<Integer>(SIZE);
22+
for (int i = 0; i < SIZE; i++) {
23+
list.add(new Integer(i));
24+
}
25+
}
26+
27+
public void writeList() {
28+
// The FileWriter constructor throws IOException, which must be caught.
29+
PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt"));
30+
31+
for (int i = 0; i < SIZE; i++) {
32+
// The get(int) method throws IndexOutOfBoundsException, which must be caught.
33+
out.println("Value at: " + i + " = " + list.get(i));
34+
}
35+
out.close();
36+
}
37+
}
38+
```
39+
40+
41+
构造函数 FileWriter 初始化文件上的输出流。如果文件无法打开,构造函数会抛出一个IOException异常。第二个对ArrayList类的get方法的调用,如果其参数的值太小(小于0)或太大(超过ArrayList当前包含的元素数量),它将抛出 IndexOutOfBoundsException。
42+
43+
如果尝试编译ListOfNumbers类,则编译器将打印有关FileWriter构造函数抛出的异常的错误消息。但是,它不显示有关get抛出的异常的错误消息。原因是构造函数IOException抛出的异常是一个检查异常,而get方法IndexOutOfBoundsException抛出的异常是未检查的异常。
44+
45+
现在,我们已经熟悉ListOfNumbers类,并且知道了其中那些地方可能抛出异常。下一步我们就可以编写异常处理程序来捕获和处理这些异常。
46+
47+
48+
## try块
49+
50+
51+
构造异常处理程序的第一步是封装可能在try块中抛出异常的代码。 一般来说,try块看起来像下面这样:
52+
53+
```java
54+
try {
55+
code
56+
}
57+
catch and finally blocks . . .
58+
```
59+
60+
示例标记 `code` 中的段可以包含一个或多个可能抛出的异常。
61+
62+
每行可能抛出异常的代码都可以用单独的一个 try 块,或者多个异常放置在一个 try 块中。 以下示例由于非常简短,所有使用一个try块。
63+
64+
```java
65+
private List<Integer> list;
66+
private static final int SIZE = 10;
67+
68+
public void writeList() {
69+
PrintWriter out = null;
70+
try {
71+
System.out.println("Entered try statement");
72+
out = new PrintWriter(new FileWriter("OutFile.txt"));
73+
for (int i = 0; i < SIZE; i++) {
74+
out.println("Value at: " + i + " = " + list.get(i));
75+
}
76+
}
77+
catch and finally blocks . . .
78+
}
79+
```
80+
81+
82+
如果在try块中发生异常,那么该异常由与其相关联的异常处理程序将会进行处理。 要将异常处理程序与try块关联,必须在其后面放置一个catch块。
83+
84+
## catch块
85+
86+
通过在try块之后直接提供一个或多个catch块,可以将异常处理程序与try块关联。 在try块的结尾和第一个catch块的开始之间没有代码。
87+
88+
```java
89+
try {
90+
91+
} catch (ExceptionType name) {
92+
93+
} catch (ExceptionType name) {
94+
95+
}
96+
```
97+
98+
99+
每个catch块是一个异常处理程序,处理由其参数指示的异常类型。 参数类型ExceptionType声明了处理程序可以处理的异常类型,并且必须是从Throwable类继承的类的名称。 处理程序可以使用名称引用异常。
100+
101+
catch块包含了在调用异常处理程序时执行的代码。 当处理程序是调用堆栈中第一个与ExceptionType匹配的异常抛出的类型时,运行时系统将调用异常处理程序。 如果抛出的对象可以合法地分配给异常处理程序的参数,则系统认为它是匹配。
102+
103+
以下是writeList方法的两个异常处理程序:
104+
105+
```java
106+
try {
107+
108+
} catch (IndexOutOfBoundsException e) {
109+
System.err.println("IndexOutOfBoundsException: " + e.getMessage());
110+
} catch (IOException e) {
111+
System.err.println("Caught IOException: " + e.getMessage());
112+
}
113+
```
114+
115+
异常处理程序可以做的不仅仅是打印错误消息或停止程序。 它们可以执行错误恢复,提示用户做出决定,或者使用异常链将错误传播到更高级别的处理程序,如“异常链”部分所述。
116+
117+
### 在一个异常处理程序中处理多个类型的异常
118+
119+
120+
在Java SE 7和更高版本中,单个catch块可以处理多种类型的异常。 此功能可以减少代码重复,并减少定义过于宽泛的异常。
121+
122+
在catch子句中,多个类型的异常使用竖线(|)分隔每个异常类型:
123+
124+
125+
```java
126+
catch (IOException|SQLException ex) {
127+
logger.log(ex);
128+
throw ex;
129+
}
130+
```
131+
132+
注意:如果catch块处理多个异常类型,则catch参数将隐式为final。 在本示例中,catch参数ex是final,因此您不能在catch块中为其分配任何值。
133+
134+
## finally 块
135+
136+
finally块总是在try块退出时执行。这确保即使发生意外异常也会执行finally块。但 finally 的用处不仅仅是异常处理 - 它允许程序员避免清理代码意外绕过 return、continue 或 break 。将清理代码放在finally块中总是一个好的做法,即使没有预期的异常。
137+
138+
注意:如果在执行try或catch代码时JVM退出,则finally块可能无法执行。同样,如果执行try或catch代码的线程被中断或杀死,则finally块可能不执行,即使应用程序作为一个整体继续。
139+
140+
141+
writeList方法的try块打开一个PrintWriter。程序应该在退出writeList方法之前关闭该流。这提出了一个有点复杂的问题,因为writeList的try块可以以三种方式中的一种退出。
142+
143+
* new FileWriter语句失败并抛出IOException。
144+
* list.get(i)语句失败,并抛出IndexOutOfBoundsException。
145+
* 一切成功,try块正常退出。
146+
147+
运行时系统总是执行finally块内的语句,而不管try块内发生了什么。所以它是执行清理的完美场所。
148+
149+
下面的finally块为writeList方法清理,然后关闭PrintWriter。
150+
151+
```java
152+
finally {
153+
if (out != null) {
154+
System.out.println("Closing PrintWriter");
155+
out.close();
156+
} else {
157+
System.out.println("PrintWriter not open");
158+
}
159+
}
160+
```
161+
162+
重要:finally块是防止资源泄漏的关键工具。 当关闭文件或恢复资源时,将代码放在finally块中,以确保资源始终恢复。
163+
164+
考虑在这些情况下使用try-with-resources语句,当不再需要时自动释放系统资源。
165+
166+
167+
## try-with-resources 语句
168+
169+
try-with-resources 是 JDK 7 中一个新的异常处理机制,它能够很容易地关闭在 try-catch 语句块中使用的资源。所谓的资源(resource)是指在程序完成后,必须关闭的对象。try-with-resources 语句确保了每个资源在语句结束时关闭。所有实现了 [java.lang.AutoCloseable](http://docs.oracle.com/javase/8/docs/api/java/lang/AutoCloseable.html) 接口(其中,它包括实现了 [java.io.Closeable](http://docs.oracle.com/javase/8/docs/api/java/io/Closeable.html) 的所有对象),可以使用作为资源。
170+
171+
例如,我们自定义一个资源类
172+
173+
```java
174+
public class Demo {
175+
public static void main(String[] args) {
176+
try(Resource res = new Resource()) {
177+
res.doSome();
178+
} catch(Exception ex) {
179+
ex.printStackTrace();
180+
}
181+
}
182+
}
183+
184+
class Resource implements AutoCloseable {
185+
void doSome() {
186+
System.out.println("do something");
187+
}
188+
@Override
189+
public void close() throws Exception {
190+
System.out.println("resource is closed");
191+
}
192+
}
193+
```
194+
195+
执行输出如下:
196+
197+
do something
198+
resource is closed
199+
200+
可以看到,资源终止被自动关闭了。
201+
202+
再来看一个例子,是同时关闭多个资源的情况:
203+
204+
```java
205+
public class Main2 {
206+
public static void main(String[] args) {
207+
try(ResourceSome some = new ResourceSome();
208+
ResourceOther other = new ResourceOther()) {
209+
some.doSome();
210+
other.doOther();
211+
} catch(Exception ex) {
212+
ex.printStackTrace();
213+
}
214+
}
215+
}
216+
217+
class ResourceSome implements AutoCloseable {
218+
void doSome() {
219+
System.out.println("do something");
220+
}
221+
@Override
222+
public void close() throws Exception {
223+
System.out.println("some resource is closed");
224+
}
225+
}
226+
227+
class ResourceOther implements AutoCloseable {
228+
void doOther() {
229+
System.out.println("do other things");
230+
}
231+
@Override
232+
public void close() throws Exception {
233+
System.out.println("other resource is closed");
234+
}
235+
}
236+
```
237+
238+
最终输出为:
239+
240+
do something
241+
do other things
242+
other resource is closed
243+
some resource is closed
244+
245+
在 try 语句中越是最后使用的资源,越是最早被关闭。
246+
247+
### try-with-resources 在 JDK 9 中的改进
248+
249+
作为 [Milling Project Coin](http://openjdk.java.net/jeps/213) 的一部分, try-with-resources 声明在 JDK 9 已得到改进。如果你已经有一个资源是 final 或等效于 final 变量,您可以在 try-with-resources 语句中使用该变量,而无需在 try-with-resources 语句中声明一个新变量。
250+
251+
例如,给定资源的声明
252+
253+
// A final resource
254+
final Resource resource1 = new Resource("resource1");
255+
// An effectively final resource
256+
Resource resource2 = new Resource("resource2");
257+
258+
老方法编写代码来管理这些资源是类似的:
259+
260+
// Original try-with-resources statement from JDK 7 or 8
261+
try (Resource r1 = resource1;
262+
Resource r2 = resource2) {
263+
// Use of resource1 and resource 2 through r1 and r2.
264+
}
265+
266+
而新方法可以是
267+
268+
// New and improved try-with-resources statement in JDK 9
269+
try (resource1;
270+
resource2) {
271+
// Use of resource1 and resource 2.
272+
}
273+
274+
看上去简洁很多吧。对 Java 未来的发展信心满满。
275+
276+
愿意尝试 JDK 9 这种新语言特性的可以下载使用 [JDK 9 快照](https://jdk9.java.net/download/)。Enjoy!
277+
278+
### 源码
279+
280+
本章例子的源码,可以在 <https://github.com/waylau/essential-java>`com.waylau.essentialjava.exception.trywithresources` 包下找到。
281+
282+
2283

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# 捕获或者声明(Catch or Specify Requirement)
2+
3+
有效的Java编程语言代码必须满足异常的捕获或者声明(Catch or Specify Requirement) 。 可能抛出异常的代码必须是下列两种处理方式之一:
4+
5+
* 使用 try 捕获异常。try 必须提供处理器来处理异常,详见“[异常捕获和处理](exceptions-catch-and-handle.md)”;
6+
* 通过在方法签名中利用 throws 关键字,声明异常可以将异常抛出,将异常传递给调用者,自己可以不用处理。详见“[通过方法声明异常抛出](exceptions-specify-exceptions-thrown.md)”。
7+
8+
所编写的代码如果不满足捕获或者声明异常将不会编译成功。
9+
10+
并非所有的异常都满足捕获或者声明异常 的约束。 为了理解为什么,我们需要看看三个基本类别的例外,其中只有一个符合要求。
11+
12+
## 三个不同的异常
13+
14+
### 1. 已检查异常(checked exception)
15+
16+
已检查异常是一个良好的应用程序应该预期和恢复的特殊条件。例如,假设应用程序提示用户输入文件名,然后通过将名称传递给`java.io.FileReader`的构造函数来打开该文件。通常,用户提供现有可读文件的名称,这样`FileReader`对象才能构造成功,应用程序才能执行正常进行。但有时用户提供不存在的文件的名称,并且构造函数抛出`java.io.FileNotFoundException`。一个编写良好的程序将捕获此异常并通知用户该错误,并可能地提示用户需要修正文件名。
17+
18+
已检查异常受限于捕获或者声明异常。除了 Error、RuntimeException 和它们的子类以外,所有异常都是已检查异常。
19+
20+
### 2. 错误(error)
21+
22+
错误是应用程序外部的特殊条件,是应用程序通常无法预期或恢复的。例如,假设应用程序成功打开文件以进行输入,但由于硬件或系统故障而无法读取该文件。未成功读取将抛出`java.io.IOError`。应用程序可能选择捕获此异常,以便将该问题通知给用户 ,当然程序也可以选择打印堆栈跟踪并退出。
23+
24+
错误不受捕获或者声明异常的限制。错误是由 Error 及其子类指示的异常。
25+
26+
### 3. 运行时异常(runtime exception)
27+
28+
运行时异常是应用程序内部的特殊情况,应用程序通常无法预期或恢复。这些通常表示编程错误,例如逻辑错误或API的不当使用。例如,考虑前面描述的将文件名传递给`FileReader`的构造函数的应用程序。如果一个逻辑错误导致一个null传递给构造函数,构造函数将抛出`NullPointerException`。应用程序可以捕获此异常,但也可能更有意义,以消除导致异常发生的错误。
29+
30+
31+
运行时异常不受捕获或者声明异常的限制。运行时异常是由 RuntimeException 及其子类指示的异常。
32+
33+
34+
错误和运行时异常统称为未检查异常(unchecked exceptions)。
35+
36+
## 绕过捕获或者声明异常
37+
38+
一些程序员认为不受捕获或者声明异常是异常机制的严重缺陷,并试图通过使用未检查的异常代替已检查异常来绕过它。 一般来说,这是不推荐。 “[未检查的异常](exceptions-unchecked-exception.md)”部分讨论何时使用未检查异常是恰当的。
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# 通过方法声明异常抛出
2+
3+
上一节展示了如何为ListOfNumbers类中的writeList方法编写异常处理程序。 有时,它适合代码捕获可能发生在其中的异常。 但在其他情况下,最好让一个方法进一步推给上层来调用堆栈处理异常。 例如,如果您将ListOfNumbers类提供为类包的一部分,则可能无法预期包的所有用户的需求。 在这种情况下,最好不要捕获异常,并允许一个方法进一步推给上层来调用堆栈来处理它。
4+
5+
如果writeList方法没有捕获其中可能发生的已检查异常,则writeList方法必须指定它可以抛出这些异常。 让我们修改原始的writeList方法来指定它可以抛出的异常,而不是捕捉它们。 请注意,下面是不能编译的writeList方法的原始版本。
6+
7+
8+
```
9+
public void writeList() {
10+
PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt"));
11+
for (int i = 0; i < SIZE; i++) {
12+
out.println("Value at: " + i + " = " + list.get(i));
13+
}
14+
out.close();
15+
}
16+
```
17+
18+
19+
要指定writeList可以抛出两个异常,请为writeList方法的方法声明添加一个throws子句。 throws子句包含throws关键字,后面是由该方法抛出的所有异常的逗号分隔列表。 该子句在方法名和参数列表之后,在定义方法范围的大括号之前。这里是一个例子。
20+
21+
```
22+
public void writeList() throws IOException, IndexOutOfBoundsException {
23+
```
24+
25+
记住 IndexOutOfBoundsException是未检查异常(unchecked exception),包括它在throws子句中不是强制性的。 你可以写成下面这样
26+
27+
```
28+
public void writeList() throws IOException {
29+
```

0 commit comments

Comments
 (0)