第七章 异常、断言和日志
1.异常分类
异常对象派生于Throwable类的一个实例。
Error 类层次结构描述了Java运行时系统内部错误和资源耗尽错误。
RuntimeException: 由程序错误导致的异常(错误的类型转换,数组访问越界,访问null指针)。
IOException: 程序本身没有问题,但由于像I/O错误导致的异常(尝试打开不存在的文件,尝试在文件尾部后面读取数据,尝试根据给定的字符串查找Class对象,但字符串表示的类不存在)。
2. 非受查异常和受查异常
Java语言规范将
1)派生于 Error类 或者 RuntimeException类 的所有异常称为非受查异常。
2)其他所有异常称为受查异常。
编译器会检查是否为所有受查异常提供了处理(抛出或者捕获)。
3. 声明受查异常
一个方法必须在首部声明所有可能抛出的异常。遇到下面4种情况时应该抛出异常:
1)调用一个抛出受查异常的方法。
2)程序运行过程中发现错误,并利用throw语句抛出一个受查异常。
3)程序出现错误抛出非受查异常。
4)Java虚拟机和运行时库出现的内部错误。
如果一个方法有可能抛出多个受查异常类型,那么必须在首部列出所有的异常类型,每个异常用逗号隔开。
tips: 子类方法声明的受查异常不能比超类方法中声明的异常更通用。如果超类方法没有抛出任何受查异常,子类也不能抛出任何受查异常。
4. 如何抛出异常
对于一个已经存在的异常类,将其抛出需要创建这个类的对象,将对象抛出。一旦方法抛出了异常,这个方法就不可能返回到调用者。
5. 捕获异常
try{ ... //若出现异常将跳过剩余代码 }catch(ExceptionType e){ handler for this type //若捕获异常e将进入执行,否则跳过改语句块 }
如果一个异常发生时没有任何地方捕获,程序将会终止执行,并在控制台打印异常信息(异常的类型和堆栈的内容)。
在catch子句块可以抛出一个异常,这么做的目的是改变异常的类型。
try{ ... }catch(SQLException e){ throw new ServletException("database error: " + e.getMessage()); }
6.finally子句
不管是否有异常被捕获,finally子句中的代码都被执行。
当finally子句中含有return语句时。假设利用return语句从try语句块中推出。在方法返回前,finally子句的内容将被执行。如果finally子句中也有一个return子句,这个返回值会覆盖原始的返回值。
public static int f(int n){ try{ int r = n*n; return r; } finally{ if(n == 2) return 0; } }
f(2)执行结果为0.
7. 打印异常堆栈信息
捕获到异常后常常会发送报警邮件,此时需要完整的异常信息方便定位问题,推荐使用org.apache.commons.lang3.exception.ExceptionUtils:
public class ErrorPrint { public static void main(String[] args) { File file = new File("//"); try (InputStream inputStream = new FileInputStream(file)) { // } catch (IOException e) { String errorMsg = ExceptionUtils.getStackTrace(e); System.out.println(errorMsg); } } }