前言
不得不说,我们平时在写程序时遇到最多的情形或者最头疼的事情就是遇到异常,然后各种调试,各种方法用尽最后才解决它,那么异常主要分为几类,每个类又有什么特征了(如图)
从大的角度说主要分成两类:Error类代表了编译和系统的错误,不允许捕获,也就是我们自己无法主动去处理它;Exception类代表了我们可捕获并且可自己可处理异常,而Exception有包括两类,一个是你必须要处理的异常,如某个类有throws异常了,在未进入运行期就必须处理,另一类就是Runtime Exception(运行期异常),就是只有在运行时(也就是编译成class文件)时才知道,这个异常可处理或不处理
处理异常机制
在java应用程序中,异常处理机制主要为两种:抛出异常和捕获异常
抛出异常
简单的说,就是不处理异常,将异常不断抛给上一级,直至某一级有有处理异常措施,一般用throws、throw如
public readFile() throws IOException{
throw new xxx();
}
捕获异常
定义是这样说的,在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适 的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。
一般有try..catch语句块,其语法格式如下:
try {
// 可能会发生异常的程序代码
} catch (Type1 id1){
// 捕获并处置try抛出的异常类型Type1
}
catch (Type2 id2){
//捕获并处置try抛出的异常类型Type2
}
如果,不管异常有没有捕获,都要执行某段代码,如数据库查询后都要关闭等等,就可以用finally,上面修改
try {
// 可能会发生异常的程序代码
} catch (Type1 id1){
// 捕获并处置try抛出的异常类型Type1
}
catch (Type2 id2){
//捕获并处置try抛出的异常类型Type2
} finally{
//必须执行的代码
}
try-catch-finally 规则
try块:用于捕获异常。其后可接0个或多个catch块,如果没有catch块,则必须跟一个finally块
catch块:用于处理try捕获到的异常。
finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。在以下4种特殊情况下,finally块不会被执行:
1)在finally语句块中发生了异常。
2)在前面的代码中用了System.exit()退出程序。
3)程序所在的线程死亡。
4)关闭CPU。
try..catch..拥有执行顺序,即第一个catch之后,后面不在执行catch,所以一般将较详细的catch放在最前面,如先捕获IOException在捕获Exception
throws规则
1) 如果是不可查异常(unchecked exception),即Error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。
2)必须声明方法可抛出的任何可查异常(checked exception)。即如果一个方法可能出现受可查异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误
3)仅当抛出了异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出
4)调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。
如下
void method1() throws IOException{} //合法
//编译错误,必须捕获或声明抛出IOException
void method2(){
method1();
}
//合法,声明抛出IOException
void method3()throws IOException {
method1();
}
//合法,声明抛出Exception,IOException是Exception的子类
void method4()throws Exception {
method1();
}
//合法,捕获IOException
void method5(){
try{
method1();
}catch(IOException e){…}
}
//编译错误,必须捕获或声明抛出Exception
void method6(){
try{
method1();
}catch(IOException e){throw new Exception();}
}
//合法,声明抛出Exception
void method7()throws Exception{
try{
method1();
}catch(IOException e){throw new Exception();}
}
这里需要注意的是throw 抛出的只能够是可抛出类Throwable 或者其子类的实例对象
Throwable类源码分析
以上源码分析来自于互联网,同时由于目前是jdk1.8和这个源码有一小部分不一样,但不影响整体分析。当然,我们真正关心的是他的下面几个方法
e.getMessage();”用于输出错误性质。通常异常处理常用3个函数来获取异常的有关信息:
getCause():返回抛出异常的原因。如果 cause 不存在或未知,则返回 null。
getMeage():返回异常的消息信息。
printStackTrace():对象的堆栈跟踪输出至错误输出流,作为字段 System.err 的值。
而当我们出现异常时,就形成一条异常链,一个一个堆栈的打印出相关信息和异常