Java之异常
简介
Java的异常处理依赖于你已经知道所调用的方法是有风险的(也就是方法可能产生异常),因此你可编写出处理此可能性的代码,如果你知道调用某个方法可能会有异常状况,你就可以与预先准备好对问题的处理程序,或者是从错误中恢复。
在Java中最好将有风险的程序代码包含在try/catch块中,这样才能对异常进行捕获和处理。
异常是一种Exception类型的对象。
格式:
1 | try{ |
创建和抛出异常
没错,我们不仅能写处理异常的程序,也可以自己创建和抛出异常,具体方式请往下看。
创建有风险、会抛出异常的程序代码:
1 | public void takeRisk() throws BadException {//必须声明它会抛出BadException |
调用该方法的程序代码
1 | public void crossFingers(){ |
如果你有抛出异常,则一定要使用throw来声明这件事。
注意:
- 编译器不会注意RuntimeException类型的异常。RuntimeExcetion不需要声明或被包含在try/catch的块中(然而你还是可以这样做)。编译器所关心的是成为检查异常(checked exception)的异常,程序必须要认识有以上可能的存在。
- 方法可以用trow关键系抛出异常对象:
throw new FileIsTooSmallException();
- 可能会抛出异常的方法必须声明成throws Exception
try/catch块的流程控制
当你调用有风险的方法时,发生的事有两种可能的情况:
- 成功的把try块完成
- 把异常丢回调用方的方法
finally:无论如何都要执行的部分
打个比方,你做菜,得把火打开,然后会有两种情况,一种是全程没问题,你顺利把菜炒完,另一种是出现状况了,比如锅漏了这种异常导致你做菜大业失败,但是不管哪种情况,发生之后都必须执行的是把炉子关掉,而关炉子这个流程就是放在finally块中的。
很明显,finally块是用来存放不管有没有异常都得执行的程序。
格式:
1 | try{ |
值得注意的是,就酸try或catch块中有return子凌,finally还是会执行,流程调到finally然后在回到return指令,就是这么厉害。
举个栗子:
1 | public class TestExceptions { |
运行结果为:
start try start risky end risky end try finally end of main 若第三行改为“String test = "no";”则结果应为: start try start risky scary exception finally end of main
多重异常
如果有必要,方法可以排除多个异常。但该方法的声明必须要有含有全部可能的检查异常(若两个或两个以上的异常有共同的父类是,可以只声明该父类就行)
格式:
1 | public class Laundry{ |
异常具有多态性
是基于类的继承关系得来的,父类或者子类的异常可以直接catch父类的异常来包含所有异常,catch子类的异常则只能用来处理子类的异常,对父类的不起作用。但是这也不意味着就可以都直接catch父类的异常来省去catch子类异常的步骤,因为直接catch父类的异常会导致不知道是哪个子类发生的异常,你会搞不清到底哪里出错了。
有多个catch块时要从大到小排列
“从大到小”指的是按照类的继承关系,从最下面的子类放第一个位置,最开始的父类放最下面。兄弟之间次序不重要,可以随便放。
duck异常
没错,不想处理的异常可以duck(躲避)掉。
方法时让调用的方法抛出异常,让调用那个方法的方法也抛出异常,就像踢皮球一样,不去管那个异常,只管抛出去,自己不管。既然都不管,这样踢来踢去最后只能落到Java虚拟机上,而Java虚拟机并不在意异常,所以程序就能编译通过。
1 | public class Test { |
以上代码如果将main函数里的throws ClothingException去掉,即没有duck掉异常,则无法通过编译,还会出现“unzepozted eception”的错误信息。如果不想duck掉,就老老实实用try/catch块将a.foo()这一危险操作包起来。
异常处理的结构规则
- catch与finally不能没有try
- try与catch之间不能有程序
- try一定要有catch或finally
- 只带有finally的try必须要声明异常
栗子:
1 | void go() throws FooException{//只带有finally的try |