[02] 异常链和自定义异常
1、异常链
1.1 throw
我们之前比喻过,说异常的抛出就像棒球中的投球,其中RuntimeException是发生意外后程序自行抛出的,假如我们想要自己抛出异常,就要使用 throw 关键字。
显式地抛出异常通常写为:
throw new xxxException();
1
1
throw new xxxException();
异常抛出后,有两种处理方式:
- 立即处理,使用 try catch 捕获并处理
- 稍后处理,丢给方法调用的上层处理,使用 throws 关键字
//try-catch 立即处理
public static void fun() {
File file = new File("C:/temp.txt");
if (!file.exists()) {
try {
throw new IOException();
} catch (IOException e) {
System.out.println("文件不存在");
}
}
}
11
1
//try-catch 立即处理
2
public static void fun() {
3
File file = new File("C:/temp.txt");
4
if (!file.exists()) {
5
try {
6
throw new IOException();
7
} catch (IOException e) {
8
System.out.println("文件不存在");
9
}
10
}
11
}
//throws 稍后处理
public static void fun() throws IOException{
File file = new File("C:/temp.txt");
if (!file.exists()) {
throw new IOException();
}
}
7
1
//throws 稍后处理
2
public static void fun() throws IOException{
3
File file = new File("C:/temp.txt");
4
if (!file.exists()) {
5
throw new IOException();
6
}
7
}
使用throw抛出异常时,不要直接使用Exception,或者相互之间有交集的异常,这样不能很好地标记异常的类型,增加了后期维护难度。
另外,在进行方法覆盖的时候要注意,假如你的某个方法覆盖了父类的某个方法,那么:
- 不可以增加新的异常,即使这个新异常时父类方法声明中的任何一个异常的子类也不行
- 不可以抛出 “被覆盖方法抛出异常” 的父类异常
例如:
- 父类Animal方法fun()抛出两个异常:IndexOutOfBoundsException、IOException
- 子类Cat覆盖方法fun()抛出异常最多两个,可以没有,同时子类抛出的异常,不能是父类抛出异常的父类,比如fun()不能抛出Exception(Exception是IndexOutOfBoundsException和IOException的父类)
可以这样理解:一个修理家电的人,他能够修理冰箱,电脑,洗衣机,电视机。 一个年轻人从他这里学的技术,就只能修理这些家电,或者更少。你不能要求他教出来的徒弟用从他这里学的技术去修理直升飞机。
1.2 throws
如果抛出当前抛出的异常不希望立即处理,则可以在方法声明处使用throws关键字,指出方法引发的异常。可以声明多种异常类型,以逗号分隔开即可。
public void test throws xxxException1, xxxException2, xxxException3 { ... }
1
1
public void test throws xxxException1, xxxException2, xxxException3 { ... }
使用了throws的方法,被调用时就必须要求对其内的异常进行处理,同样,要么使用try-catch,要么继续使用throws往上抛,这种不断使用throws往上抛出异常直到被处理,就形成了一个所谓抽象的概念 “异常链”。
我们有时候在捕获一个异常后会抛出一个新的异常信息,并且希望将原始的异常信息也保存起来,这个时候也需要使用异常链。在Throwable及其子类中的构造器中都可以接受一个cause参数,该参数保存了原有的异常信息,通过getCause()就可以获取该原始异常信息。
public class Test {
public void f() throws MyException{
try {
FileReader reader = new FileReader("G:\\myfile\\struts.txt");
Scanner in = new Scanner(reader);
System.out.println(in.next());
} catch (FileNotFoundException e) {
//e 保存异常信息
throw new MyException("文件没有找到--01", e);
}
}
public void g() throws MyException{
try {
f();
} catch (MyException e) {
//e 保存异常信息
throw new MyException("文件没有找到--02", e);
}
}
public static void main(String[] args) {
Test t = new Test();
try {
t.g();
} catch (MyException e) {
e.printStackTrace();
}
}
}
33
1
public class Test {
2
public void f() throws MyException{
3
try {
4
FileReader reader = new FileReader("G:\\myfile\\struts.txt");
5
Scanner in = new Scanner(reader);
6
System.out.println(in.next());
7
} catch (FileNotFoundException e) {
8
//e 保存异常信息
9
throw new MyException("文件没有找到--01", e);
10
}
11
}
12
13
14
public void g() throws MyException{
15
try {
16
f();
17
} catch (MyException e) {
18
//e 保存异常信息
19
throw new MyException("文件没有找到--02", e);
20
}
21
}
22
23
24
public static void main(String[] args) {
25
Test t = new Test();
26
try {
27
t.g();
28
} catch (MyException e) {
29
e.printStackTrace();
30
}
31
}
32
33
}
com.test9.MyException: 文件没有找到--02
at com.test9.Test.g(Test.java:31)
at com.test9.Test.main(Test.java:38)
Caused by: com.test9.MyException: 文件没有找到--01
at com.test9.Test.f(Test.java:22)
at com.test9.Test.g(Test.java:28)
... 1 more
Caused by: java.io.FileNotFoundException: G:\myfile\struts.txt (系统找不到指定的路径。)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:106)
at java.io.FileInputStream.<init>(FileInputStream.java:66)
at java.io.FileReader.<init>(FileReader.java:41)
at com.test9.Test.f(Test.java:17)
... 2 more
14
1
com.test9.MyException: 文件没有找到--02
2
at com.test9.Test.g(Test.java:31)
3
at com.test9.Test.main(Test.java:38)
4
Caused by: com.test9.MyException: 文件没有找到--01
5
at com.test9.Test.f(Test.java:22)
6
at com.test9.Test.g(Test.java:28)
7
... 1 more
8
Caused by: java.io.FileNotFoundException: G:\myfile\struts.txt (系统找不到指定的路径。)
9
at java.io.FileInputStream.open(Native Method)
10
at java.io.FileInputStream.<init>(FileInputStream.java:106)
11
at java.io.FileInputStream.<init>(FileInputStream.java:66)
12
at java.io.FileReader.<init>(FileReader.java:41)
13
at com.test9.Test.f(Test.java:17)
14
... 2 more
如果在程序中,去掉e,也就是:throw new MyException("文件没有找到--02"); 那么异常信息就保存不了,运行结果如下:
com.test9.MyException: 文件没有找到--02
at com.test9.Test.g(Test.java:31)
at com.test9.Test.main(Test.java:38)
3
1
com.test9.MyException: 文件没有找到--02
2
at com.test9.Test.g(Test.java:31)
3
at com.test9.Test.main(Test.java:38)
2、自定义异常
Java已经给我们提供了很多不同种类的异常,但是即使如此,也并不能预见所有的异常类型,特别是我们在业务上出现的错误。所以Java允许我们自定义异常,来表现程序中遇到的一些特殊问题。
我们自定义的异常类,主要还是用于标记业务逻辑的问题,避免与标准异常混淆。定义也很简单,只需要继承 Exception 即可:
public class MyException extends Exception {
public MyException(String msg){
super(msg);
}
}
x
1
2
public class MyException extends Exception {
3
4
public MyException(String msg){
5
super(msg);
6
}
7
8
}
自定义异常中往往不写其他的方法,只需要重载使用的构造方法。当然,也可以不写,使用默认的构造方法即可。
自定义异常往往是通过 throw 进行抛出使用。