# 异常分类

所有的异常都继承自 Throwable 类。

Error 类描述了 Java 运行时系统的内部错误和资源耗尽错误。此种异常不应该由程序抛出,若出现这种异常,应通告给用户并尽力使程序安全地终止。

Exception 类是应用程序抛出的错误。Exception 类包括 IO 异常类和运行时异常类。

RuntimeException 类包括错误的类型转换、数组访问越界和访问 null 指针。在程序中应该杜绝此类异常的发生。

IOException 类包括试图在文件尾部后面读取数据、试图打开一个不存在的文件和试图根据给定的字符串查找 Class 对象,而这个字符串表示的类并不存在。此类异常需要在程序中手动抛出。

Error 类或 RuntimeException 类的所有异常称为不可查异常,所有其他异常称为可查异常。一个方法必须声明所有可能抛出的可查异常,而非可查异常要么不可控制,要么就应该避免发生。

# 使用异常机制的技巧

  • 异常处理不能代替简单的测试。

    如果要对一个栈进行退栈操作,可能存在的异常是这个栈是一个空栈。那么我们最好使用 !s.empty() 来检查该栈是否为空,若不为空再进行退栈,而不应该使用抛出异常的方式。

    //right,运行时间 646ms
    if (!s.empty()) {
        s.pop();
    }
    //error,运行时间 21739ms
    try {
        s.pop();
    } catch (EmptyStackException e) {
        ...
    }
  • 不要过分地细化异常

    对一个栈进行退栈操作,然后把这个值放在一个文件里。这两步操作都有可能出现异常,如果分别进行捕获,当第一步出现异常时,程序会继续去执行第二个操作,这样就不没有什么意义了,我们希望下一步操作也终止。

    //error,代码臃肿
    PrintStream out;
    Stack s;
    for (i = 0;i < 100; i++) {
        try {
        	n = s.popO;
        } catch (EmptyStackException e) {
        	...
        } 
        
        try {
        	out.writelnt(n);
        } catch (IOException e) {
        	...
        }
    }
    //right,任何一个操作出现问题,整个任务可以取消
    try {
        for (i = 0; i < 100; i++) {
        	n = s.popO ;
        	out.writelnt(n);
        }
    } catch (IOException e) {
        ...
    } catch (EmptyStackException e) {
        ...
    }
  • 利用异常层次结构

    不要只抛出 RuntimeException 异常,应该寻找更加适合的子类或创建自己异常类;不要只捕获 Thowable 异常,否则会使程序代码更难读和维护;不要为错误逻辑抛出异常;可将一种异常转化为另一种更合适的异常,例如,在解析某个文件中的一个整数时,捕获 NumberFormatException 异常,然后将它转换成 IOException 或 MySubsystemException 的子类。

  • 不要压制异常

    若一个方法抛出一个异常,另一个方法在调用它的时候,就必须对这个异常进行处理,否则编译不会通过。但是这个异常可能

  • 在检测错误时要苛刻一点

    假如要从栈中拿出一个值,然后进行操作。若栈是一个空栈,就会出现异常。我们会在代码中进行检查,是返回一个 null 值呢,还是抛出一个异常呢,此时不抛异常,后面也要抛一个空指针异常,所以最好是在前面就抛异常,这样抛出来的异常更清晰明了,后续也不会再执行一些无用的操作。

  • 不要羞于传递异常

    有时候传递这个异常比捕获这个异常更为合适,传递下去可以让更高层次的方法通知用户发生了错误。