[02] 异常链和自定义异常


1、异常链

1.1 throw

我们之前比喻过,说异常的抛出就像棒球中的投球,其中RuntimeException是发生意外后程序自行抛出的,假如我们想要自己抛出异常,就要使用 throw 关键字。
“野球 イラスト 動物”的图片搜索结果

显式地抛出异常通常写为:
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("文件不存在");
        }
    }
}

//throws 稍后处理
public static void fun() throws IOException{
    File file = new File("C:/temp.txt");
    if (!file.exists()) {
        throw new IOException();
    }
}

使用throw抛出异常时,不要直接使用Exception,或者相互之间有交集的异常,这样不能很好地标记异常的类型,增加了后期维护难度。


另外,在进行方法覆盖的时候要注意,假如你的某个方法覆盖了父类的某个方法,那么:
  • 不可以增加新的异常,即使这个新异常时父类方法声明中的任何一个异常的子类也不行
  • 不可以抛出 “被覆盖方法抛出异常” 的父类异常

例如:
  • 父类Animal方法fun()抛出两个异常:IndexOutOfBoundsException、IOException
  • 子类Cat覆盖方法fun()抛出异常最多两个,可以没有,同时子类抛出的异常,不能是父类抛出异常的父类,比如fun()不能抛出Exception(Exception是IndexOutOfBoundsException和IOException的父类)

可以这样理解:一个修理家电的人,他能够修理冰箱,电脑,洗衣机,电视机。 一个年轻人从他这里学的技术,就只能修理这些家电,或者更少。你不能要求他教出来的徒弟用从他这里学的技术去修理直升飞机。
“修理家电”的图片搜索结果

1.2 throws

如果抛出当前抛出的异常不希望立即处理,则可以在方法声明处使用throws关键字,指出方法引发的异常。可以声明多种异常类型,以逗号分隔开即可。
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();
        }
    }

}

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

如果在程序中,去掉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)


2、自定义异常

Java已经给我们提供了很多不同种类的异常,但是即使如此,也并不能预见所有的异常类型,特别是我们在业务上出现的错误。所以Java允许我们自定义异常,来表现程序中遇到的一些特殊问题。

我们自定义的异常类,主要还是用于标记业务逻辑的问题,避免与标准异常混淆。定义也很简单,只需要继承 Exception 即可

public class MyException extends Exception {
    
    public MyException(String msg){
        super(msg);    
    }    
    
} 

自定义异常中往往不写其他的方法,只需要重载使用的构造方法。当然,也可以不写,使用默认的构造方法即可。

自定义异常往往是通过 throw 进行抛出使用。


3、参考链接


posted @ 2017-09-01 11:40  Dulk  阅读(290)  评论(0编辑  收藏  举报