违例差错控制

违例差错控制这个给程序的编写带来了很大的好处:

 1 public class test {
2 public static void f() throws Exception {
3 System.out.println(
4 "originating the exception in f()");
5 throw new Exception("thrown from f()");
6 }
7 public static void g() throws Throwable {
8 try {
9 f();
10 } catch(Exception e) {
11 System.out.println(
12 "Inside g(), e.printStackTrace()");
13 e.printStackTrace();
14 throw e;
15 }
16 }
17 public static void main(String[] args) throws Throwable {
18 try {
19 g();
20 } catch(Exception e) {
21 System.out.println(
22 "Caught in main, e.printStackTrace()");
23 e.printStackTrace();
24 }
25 }
26 }
output:
java.lang.Exception: thrown from f()
originating the exception in f()
Inside g(), e.printStackTrace()
	at test.f(test.java:5)
	at test.g(test.java:9)
	at test.main(test.java:21)
java.lang.Exception: thrown from f()
	at test.f(test.java:5)
	at test.g(test.java:9)
	at test.main(test.java:21)
Caught in main, e.printStackTrace()

违例路径堆栈无论如何都会记住它的真正起点,无论自己被重复“投掷”了多少次。printStackTrace()在什么时候会被执行呢?为什么在不同的时候输出的顺序并不相同?什么时候记录调用记录的?从这个程序中都可以看出来了。再来一个例子说明问题:

 1 public class test {
2 public static void f() throws Exception {
3 System.out.println(
4 "originating the exception in f()");
5 throw new Exception("thrown from f()");
6 }
7 public static void main(String[] args) {
8 try {
9 f();
10 } catch(Exception e) {
11 System.out.println(
12 "Caught in main, e.printStackTrace()");
13 e.printStackTrace();
14 throw new NullPointerException("from main");
15 }
16 }
17 }
output
originating the exception in f()
Caught in main, e.printStackTrace()
java.lang.Exception: thrown from f()
	at test.f(test.java:5)
	at test.main(test.java:9)
Exception in thread "main" java.lang.NullPointerException: from main
	at test.main(test.java:14)

异常和异常之间只是名字的不同(所以名字很重要)。Throwable有两种常规类型:Exception和Error。Error是指编译期和系统错误,我们不必特意捕获他们。Exception是可以从任何标准Java库的类方法中“throw”出的基本类型。RuntimeException在默认情况下自动会处理,如果必须检查RuntimeException那我们的代码就会变得相当复杂(难道我们时不时顶着空指针还得自己处理?)。一个例子:

 1 public class test {
2 static void f() {
3 throw new RuntimeException("From f()");
4 }
5 static void g() {
6 f();
7 }
8 public static void main(String[] args) {
9 g();
10 }
11 }
output
Exception in thread "main" java.lang.RuntimeException: From f()
	at test.f(test.java:3)
	at test.g(test.java:6)
	at test.main(test.java:9)

如果RuntimeException获得到达main()的所有途径,同时不被捕获,那么程序在退出的时候会自动调用printStackTrace()。

创建自己的“Exception”还是很简单的吧,举个例子:

 1 class MyException extends Exception {
2 public MyException() {}
3 public MyException(String msg) {
4 super(msg);
5 }
6 }
7 public class test {
8 public static void f() throws MyException {
9 System.out.println(
10 "Throwing MyException from f()");
11 throw new MyException();
12 }
13 public static void g() throws MyException {
14 System.out.println(
15 "Throwing MyException from g()");
16 throw new MyException("Originated in g()");
17 }
18 public static void main(String[] args) {
19 try {
20 f();
21 } catch(MyException e) {
22 e.printStackTrace();
23 }
24 try {
25 g();
26 } catch(MyException e) {
27 e.printStackTrace();
28 }
29 }
30 }
output
MyException
	at test.f(test.java:11)
	at test.main(test.java:20)
MyException: Originated in g()
	at test.g(test.java:16)
	at test.main(test.java:25)Throwing MyException from f()
Throwing MyException from g()

如何限制违例?在捕获到一个异常的时候怎么知道我们抓到的东西是正确的东西:

 1 class BaseballException extends Exception {}
2 class Foul extends BaseballException {}
3 class Strike extends BaseballException {}
4 abstract class Inning {
5 Inning() throws BaseballException {}
6 void event () throws BaseballException {
7 }
8 abstract void atBat() throws Strike, Foul;
9 void walk() {}
10 }
11 class StormException extends Exception {}
12 class RainedOut extends StormException {}
13 class PopFoul extends Foul {}
14 interface Storm {
15 void event() throws RainedOut;
16 void rainHard() throws RainedOut;
17 }
18 public class test extends Inning implements Storm {
19 test() throws RainedOut, BaseballException {}
20 test(String s) throws Foul, BaseballException {}
21 public void rainHard() throws RainedOut {}
22 public void event() {}
23 void atBat() throws PopFoul {}
24 public static void main(String[] args) {
25 try {
26 test si = new test();
27 si.atBat();
28 } catch(PopFoul e) {
29 } catch(RainedOut e) {
30 } catch(BaseballException e) {
31 }
32 try {
33 Inning i = new test();
34 i.atBat();
35 } catch(Strike e) {
36 } catch(Foul e) {
37 } catch(RainedOut e) {
38 } catch(BaseballException e) {
39 }
40 }
41 }

Java提供了用finally来清除(也是一个从异常中恢复运行的例子):

 1 public class test {
2 static int count = 0;
3 public static void main(String[] args) {
4 while(true) {
5 try {
6 if(count++ == 0)
7 throw new Exception();
8 System.out.println("No exception");
9 } catch(Exception e) {
10 System.out.println("Exception thrown");
11 } finally {
12 System.out.println("in finally clause");
13 if(count == 2) break;
14 }
15 }
16 }
17 }
output
Exception thrown
in finally clause
No exception
in finally clause

finally中的代码不管是在什么情况下都是会执行的。finally能做什么呢?比如打开文件或者套接字的时候,在finally中释放这些资源。如果没有finally要怎么实现:

 1 class Switch {
2 boolean state = false;
3 boolean read() { return state; }
4 void on() { state = true; }
5 void off() { state = false; }
6 }
7 public class test {
8 static Switch sw = new Switch();
9 public static void main(String[] args) {
10 try {
11 sw.on();
12 sw.off();
13 } catch(NullPointerException e) {
14 System.out.println("NullPointerException");
15 sw.off();
16 } catch(IllegalArgumentException e) {
17 System.out.println("IOException");
18 sw.off();
19 }
20 }
21 }

可以看出finally的好处了吧。即使异常不在当前的“catch”中捕获,“finally”都会在违例控制机制转到更高级别搜索一个控制器之前得到执行。eg:

 1 class Ex extends Exception {}
2 public class test {
3 public static void main(String[] args) {
4 System.out.println("Entering first try block");
5 try {
6 System.out.println("Entering second try block");
7 try {
8 throw new Ex();
9 } finally {
10 System.out.println("finally in 2nd try block");
11 }
12 } catch(Ex e) {
13 System.out.println("Caught Ex in first try block");
14 } finally {
15 System.out.println("finally in 1st try block");
16 }
17 }
18 }
output
Entering first try block
Entering second try block
finally in 2nd try block
Caught Ex in first try block
finally in 1st try block

Java的异常控制机制还是有缺陷滴,一个异常可能被简单地“丢弃”,eg:

 1 class VeryImportantException extends Exception {
2 public String toString() {
3 return "A very important exception!";
4 }
5 }
6 class HoHumException extends Exception {
7 public String toString() {
8 return "A trivial exception";
9 }
10 }
11 public class test {
12 void f() throws VeryImportantException {
13 throw new VeryImportantException();
14 }
15 void dispose() throws HoHumException {
16 throw new HoHumException();
17 }
18 public static void main(String[] args)
19 throws Exception {
20 test lm = new test();
21 try {
22 lm.f();
23 } finally {
24 lm.dispose();
25 }
26 }
27 }
output
Exception in thread "main" A trivial exception
	at test.dispose(test.java:16)
	at test.main(test.java:24)

这样的话“try”块中的异常就被忽略掉了,而输出的异常是“finally”模块中的。用一个例子来看看清除有构建器的文件、套接字等资源的释放(感觉这个还是不错的):

 1 import java.io.*;
2 class InputFile {
3 private BufferedReader in;
4 InputFile(String fname) throws Exception {
5 try {
6 in = new BufferedReader(new FileReader(fname));
7 } catch(FileNotFoundException e) {
8 System.out.println("Could not open " + fname);
9 throw e;
10 } catch(Exception e) {
11 try {
12 in.close();
13 } catch(IOException e2) {
14 System.out.println("in.close() unsuccessful");
15 }
16 throw e;
17 } finally {}
18 }
19 String getLine() {
20 String s;
21 try {
22 s = in.readLine();
23 } catch(IOException e) {
24 System.out.println("readLine() unsuccessful");
25 s = "failed";
26 }
27 return s;
28 }
29 void cleanup() {
30 try {
31 in.close();
32 } catch(IOException e2) {
33 System.out.println("in.close() unsuccessful");
34 }
35 }
36 }
37 public class test {
38 public static void main(String[] args) {
39 try {
40 InputFile in = new InputFile("test.java");
41 String s;
42 int i = 1;
43 while((s = in.getLine()) != null)
44 System.out.println(""+ i++ + ": " + s);
45 in.cleanup();
46 } catch(Exception e) {
47 System.out.println("Caught in main, e.printStackTrace()");
48 e.printStackTrace();
49 }
50 }
51 }

可以用异常来做些什么:

  1. 解决问题并再次调用引起异常的方法;
  2. 平息事态的发展,并在不重新尝试的情况下继续;
  3. 计算另一些结果,而不是希望方法产生的结果;
  4. 在当前环境中尽可能地处理,将相同的异常重新“throw”到一个更高级的环境;
  5. 终止程序的执行;
  6. 简化代码;
  7. 使自己的库和程序更加安全。

-----------------------------

个人理解,欢迎拍砖。

原文地址:https://www.cnblogs.com/ggzwtj/p/2123872.html