前言
错误是代码难以避免的问题。编译器的错误可以直接修改代码,运行期的错误就需要将其传递到其他能处理的地方,这时候就要用到Java的异常机制。
通过异常处理错误
本书第十二章,主要讲述了Java异常及异常处理的相关内容。
Java异常
Java异常都继承自Throwable,然后其下分为Exception和Error两个子类
Error(错误)
Error指一些无法恢复或不能捕捉的严重错误,此类错误会导致程序中断。Exception(异常)
Exception又分为运行时异常和非运行时异常运行时异常
继承自Exception子类RuntimeException的异常类,可以被捕捉处理,也可以不处理。
常见的包括:NullPointerException,IndexOutOfBoundsException等非运行时异常
继承自Exception的非RuntimeException的异常类,统称为非运行时异常。
这种异常必须要捕捉处理,否则编译无法通过。
常见的如:IOException
异常的处理
抛出异常
异常抛出的关键字有两个throw和throws,他们的区别很大:
throw:强调的是抛出异常的这个动作,只在方法体中使用
throws:是对方法可能抛出异常的声明,相当于告诉其他开发者这个方法可能抛出什么异常,需要处理1
2
3
4// 手动抛出异常,程序中止
throw new MyException("10000","error");
// 声明方法可能抛出的异常,可以声明多个以逗号分隔
void method(int i) throws MyException, IOException;捕获处理异常
1 | try {// 执行逻辑 |
用catch捕捉异常可以通过捕捉对应异常的父类异常,例如上述例子中可以这么写
1 | try {// 执行逻辑 |
因为Exception是其他异常的父类,所以可以捕捉到方法抛出的两种异常。
当然,虽然这样看上去更加简洁,但是异常信息就不够明确,所以一般情况不建议这样做。
因此,catch异常的顺序也需要特别注意,如果把大的异常(即父类异常)放到前面,由于对异常的匹配
寻找是按顺序来的,后面的小异常可能永远不会派上用场。
自定义异常
除了Java已经定义好的异常类,我们也可以根据自己的需求自定义异常类
1 | // 自定义异常需要继承Exception或其子类 |
异常的栈轨迹
通常我们用e.printStackTrace();输出异常信息的时候可以看到一行行的方法地址。这就是异常的栈轨迹,表示异常一步步抛出到最外面的轨迹。
1 | public void method(float f) { |
比如上面这样层层调用,通过e.getStackTrace()获取栈轨迹的数组,然后逐个输出,就能看到如下结果
method
numberTwo
numberThree
main
能够看到每一步调用的方法。
异常链
如果捕捉异常后,抛出新的异常,那么会丢失原异常的信息。
这时可以通过Exception的构造方法保留原异常的信息:
1 | public Exception(String message, Throwable cause) { |
cause参数用来表示原始异常,可以通过这种方法将不同的异常信息串联起来,也被称作异常链。
小结
异常不可避免,但是通过抛出/捕捉处理我们可以自由的控制异常。将他抛到我们需要处理的地方,或者直接
捕捉进行处理。这使得代码逻辑能够按照我们所设想的逻辑执行,而不会因为突如其来的异常导致程序中止,偏
离了我们的预想。
另一方面,对异常的信息输出也很关键了。良好的输出代表着代码有效的反馈,能够帮助开发者快速的定位问
题,然后决定如何修改优化。