异常处理
复习
什么是异常
所谓的“异常”是在Java程序当中出现的一种不正常的情况,但是又有别于“报红线”。
在编译期,大部分报红线是“语法错误”;只有出现“未处理的异常 某某Exception”才属于Java中单独规范的“异常”内容。
在运行期,出现异常信息的打印效果,并终止掉我们的程序运行,那么这里也是发生了异常。异常打印效果如下:
Exception in Thread “main” 异常类的限定名:异常的详细信息
at 异常发生的位置(某篇.java:行)
at 异常发生的位置(某篇.java:行)
at 异常发生的位置(某篇.java:行)
at 异常发生的位置(某篇.java:行)
at 异常发生的位置(某篇.java:行)
异常的分类
对于我们来说,异常是Java当中专门为针对各种可能出现的不正常情况设计的一整套Java类。它们呈现出了一种继承结构。
Throwable
Exception Error
RuntimeException 其他
区分:
1、Exception 和 Error
2、运行时异常 和 编译时异常
运行时异常 在编译期是不会报的,而是在运行期执行的时候发生。 编译时异常 在编译期就会提示我们这里(方法调用处),有可能出现异常,必须先有处理方案,然后才能编译通过。
运行时异常一旦发生,代码中又没有处理方案,那么会终止程序的执行。
编译时异常是希望我们在编写的时候就提供解决方案,否则根本不能运行。
异常的传播机制
当JVM在运行过程中,一旦发现这里出现异常情况,那么就会主动根据当前异常情况产生对应的异常类对象。然后执行异常的处理方案,如果当前位置没有处理,那么JVM就会结束本方法,然后根据方法调用栈流程转到方法调用处。然后继续查看这里有没有该异常类的解决方案,如果还是没有,那就继续往上传播。
如果整个代码都没有该异常的解决方案,那么最终的效果就是一定来到main方法检查是否解决。如果还是没有,那么会同样终止main方法的执行。而main方法被终止,代表着程序被结束,然后JVM在关闭前就会把异常信息打印在控制台。
关联知识点:打印异常信息,是调用异常父类Throwable当中的printStackTrace()。
异常的处理方案
出现异常不可怕,解决就行。
事前处理
这种处理方案就是让异常根本不发生。在我们写代码的时候就提前把异常出现的可能给解决掉。
几乎所有的运行时异常,都应该采用这种提前做if判断的方式来解决。
只是由于现在我们学到的内容不足,导致有些能够提前解决的手段不足,因此在目前的练习和讲解中采用了事后处理的方案。
事后处理
自己处理(try-catch-finally)
try{}的任务是书写正常逻辑的代码(当然也是有可能发生异常的代码块),我们试着让他执行。
catch(){}的任务是捕获异常,并且书写捕获后要执行的处理动作。注意:就算{}中不写代码,也算把异常捕获了。
finally{}的任务是书写不管是否发生异常都要执行的动作,通常是“资源清理”,“管道关闭”,“连接的断开”......
组合上:
1、try-catch
2、try-catch-finally
3、try-finally
我们需要清楚它们三者执行的流程:
- 发生异常 -- try块会从发生异常的代码行跳转,然后用异常对象依次匹配每一个catch块当中声明的捕获类型。“依次”和“每一个”说明:一个try后面可以有多个catch,多个catch是有顺序的(有继承关系的异常,要先写子后写父)。
匹配上了,就进入该catch块执行。
补充了一个JDK7的语法:一个catch块可以用"|"声明捕获多种异常类。
执行完catch块以后就进入finally块执行,然后异常处理的流程结束。
- 没有发生异常:try块里面的代码会全部执行,然后进入finally块,最后异常处理流程结束。
考察点:
a. try-catch-finally的流程
b. catch块的特点,包括可不可以写多个,有没有顺序.......
c. finally的特点,包括执行顺序,能不能阻止,相似关键词的分辨final和finalize,如果try或catch当中有return,那么fianlly在return 之前还是之后?
交给上层处理(throw throws)
throw 书写在方法的实现体当中是一条执行指令,代表当这条指令执行的时候真的会发生一个异常对象的抛出。 throw后面跟的是一个异常对象。
throws 书写在方法声明的最后,表示本方法有可能抛出某些类型的异常。 它的后面跟的是异常类的名字。
两个的关联是:如果在一个方法的内部throw了一个编译时异常对象,那么就要求该方法的声明处必须要有throws。
关联知识点:
- 一个完整的方法声明:
修饰符 返回类型 方法名(形参列表) throws 异常类
- 方法重写的规范被增加条件了
2-1、方法名保持一致;
2-2、参数列表保持一致;
2-3、返回类型保持一致;
2-4、访问修饰符重写后不能比重写前小
2-5、重写后不能比重写前抛出更多的异常,这里的更多指的是范围,不是个数。
如何选择自己处理还是往上抛
选择的依据是:看这个异常引起的原因,是当前方法本身引起的那么肯定是当前方法自己处理;如果是上层调用者引起的,就应该抛上去,让调用者处理。
自定义异常
场景:虽然在Java的内库当中,设计了很多异常类,应对各种情况。但是,还是有很多场景中有与业务内容相关的异常,是它不可能涉及到的。所以,这种情况下,我们就需要自定义异常了。
语法
1、让类继承于Exception或它的某个子类;
2、给这个类增加带参构造
3、还可以给这个异常增加一些工具方法,比如:日志的书写。