概述
导语: 代码运行时,总会出现错误或者是异常。因此,Java从C++继承了以面向对象方式处理异常的机制,用对象的方式来表示一个(或一类)异常,从而使开发人员写出具有容错性的健壮的代码。
异常概述
Java异常体系结构
首先介绍一下Java异常体系结构,如下图所示:
在Java中,任何异常都是Throwable类或其子类对象;Throwable有两个子类,分别是:Error和Exception; Error:这是系统错误类,是程序运行时Java内部的错误,一般是由硬件或操作系统引起的,开发人员一般无法处理,这类异常发生时,只能关闭程序。 Exception:这是异常类,该类及其子类对象表示的错误一般是由算法考虑不周或编码时疏忽所致,需要开发人员处理。
Java异常分类
Java中的异常被分为两大类:Runtime异常和非Runtime异常,其中非Runtime异常也被称作Checked异常。 Runtime异常:是Java程序运行时产生的异常,例如:数组下标越界、空指针异常、对象类型强制转换错误等。Error和RuntimeException都是Runtime异常,编译时对这类异常不做检查。 非Runtime异常:也称Checked异常,例如IOException等。在编译时,编译器会对这类异常进行检查,看看有没有对这类异常进行处理,如果没有进行处理,编译会无法通过。
异常处理机制
异常的捕获与处理
Java采用try-catch-finally语句来对异常进行捕获并处理。该语句的语法格式如下:
try{
//可能会产生异常的代码
}
catch(Exception_1 e1){
//处理Exception_1的代码
}
catch(Exception_2 e2){
//处理Exception_2的代码
}
……
catch(Exception_n en){
//处理Exception_n的代码
}
finally{
//通常是释放资源的代码
}
复制代码
其中catch和finally都是可以默认的(即可以不写),但是不允许同时默认catch和finally。 try语句块:该语句块中是程序正常情况下应该要完成的功能,而这些代码中可能会产生异常,其后面的catch语句块就是用来捕获并处理这些异常的。 catch语句块:该语句块用来捕获并处理try语句块中产生的异常。每个catch语句块声明其能处理的一种特定类型的异常,catch后面的括号中就是该特定类型的异常;不过,在Java7以前,每个catch语句块只能捕获一种异常,但是,从Java7开始就支持一个catch捕获多种异常,多个异常之间用|隔开。写法如下:
try{
//可能会产生异常的代码
}
catch(Exception_1 | Exception_2 | Exception_3 | ... | Exception_n e1){
//统一处理Exception_1、Exception_2、Exception_3、...、Exception_n的代码
}
finally{
//通常是释放资源的代码
}
复制代码
catch语句块中声明的异常对象(catch(Exception_n e))封装了该异常的信息,可以通过该异常对象的一些方法来获取这些信息: String getMessage():用来获取有关异常事件的详细信息。 void printStackTrace():用来跟踪异常事件发生时执行堆栈的内容。 finally语句块:该语句块一般用于释放资源等操作。无论try语句块中是否有异常,finally语句块都会执行。
try-catch-finally执行流程(最基本的情况)
这里先介绍一下try-catch-finally执行流程最基本的情况,所谓最基本的情况就是没有用到throw关键字(抛出异常,后面会讲到)、return关键字等。 首先,执行try中的代码,如果 try中的代码没有发生异常,那么catch中的代码就不执行,等try中的代码执行完毕后直接执行finally中的代码;如果 try中的代码发生了异常(假设发生异常的代码语句是xxx),那么try中xxx下面的代码就不会执行了,会立即跳转到catch中去匹配异常,若匹配到了对应的catch中声明的异常对象,那么就执行该catch语句块中的代码,并且后面的catch语句块就不再执行。等catch语句块中的代码执行完毕,就执行finally语句块中的代码。此时,整个try-catch-finally才算执行完毕。
声明抛出异常
声明抛出异常是一个子句,它只能写在方法头部的后面,其格式如下: throws 举例:public void fun() throws IOException{......} 若在一个方法中声明抛出异常,那么调用该方法的调用者就必须对该异常进行处理,调用者处理该异常有两种方式:
使用try-catch-finally来捕获并处理该异常。
继续抛出,留给后面的调用者去处理,从而形成异常处理链。
抛出异常
上面讲到的声明抛出异常只是告诉方法的调用者要去处理异常,这只是个说明性的语句,因为方法的代码中可能有异常,也可能没有异常(没有异常时就不会抛出)。 而真正抛出异常的语句是throw ,其中的异常异常对象必须是Throwable或其子类对象。例如: throw new Exception("这是一个异常对象!") 当执行到上述语句时,会立即结束方法的执行。 接下来通过案例来讲解,请看下面的代码:
@Test
public void test1(){
try{
int a=1/0; //发生异常的代码
System.out.println("算术异常!!!");
}
catch(ArithmeticException | NumberFormatException e){
System.out.println(e.getMessage()); //打印异常信息,并没有抛出异常
throw new ArithmeticException("抛出算术异常!!!"); //抛出算术异常
}
finally{
System.out.println("执行finally代码块!!");
}
System.out.println("已经抛出算术异常!!!");
}
复制代码
上述代码的运行结果如下图所示:
try中的int a=1/0;语句发生了算术异常(除数为0),直接跳转到catch中去匹配异常对象,匹配到了ArithmeticException异常对象,然后执行catch中的代码:打印异常信息,抛出异常。但是,在catch中抛出异常之前,会先执行finally中的代码,等finally中的代码执行完毕,再回到catch中抛出异常,而catch中的异常抛出后,整个方法就结束了,方法中剩下的代码就不执行了。 以上就是上述代码的完整执行流程。
异常屏蔽问题
如果在try-catch-finally代码块中,try、catch以及finally中都有异常抛出,那么最终只能抛出finally代码块中的异常。这就是异常屏蔽问题。 请看下面的代码:
//如果try、catch以及finally中都有异常抛出,那么最终只能抛出finally中的异常,try和catch中的异常抛出会被屏蔽
@Test
public void test4(){
try{
System.out.println("try");
throw new RuntimeException("try中抛出异常!!!");
}
catch(Exception e){
System.out.println("catch");
throw new RuntimeException("catch中抛出异常!!!");
}
finally{
System.out.println("finally");
throw new RuntimeException("finally中抛出异常!!!");
}
}
复制代码
上述代码的运行结果为:
首先执行try中的代码:打印“try”;然后在try中抛出异常,那么在try中的异常抛出之前,要去catch中匹配异常对象并执行catch代码块,于是,匹配到了Exception对象并执行catch中的代码:打印“catch”,然后抛出异常;那么,在抛出catch中的异常之前,要先执行finally中的代码:打印“finally”,并抛出finally中的异常,此时整个方法执行结束,前面的两个异常不会被抛出。这就是所谓的异常屏蔽。
其他问题
如果在tr-catch-finally代码块中有return,那么执行流程又是怎样的呢? 请看下面的代码:
public int test5(){
try{
int a=1/0; //发生异常的代码
return 1;
}
catch(Exception e){
System.out.println("catch");
return 2;
}
finally{
System.out.println("finally");
return 3; //禁止在finally中使用return语句,这里只是举例说明,在实际开发中禁止使用
}
}
复制代码
在main方法中执行上述方法,结果为:
try中int a=1/0;语句发生了异常,那么就跳转到catch中去匹配并执行catch中的代码,而catch中要返回整数2,在执行catch中的return 2;语句之前,要先去执行finally中的代码,因为finally中有return 3;语句,所以当执行到finally中的return语句后,整个方法就结束了,catch以及try中的return语句就不会执行了。 请继续看下面的代码:
public int test8(){
// try中没有异常发生
try{
int a=1/1;
return 1;
}
catch(Exception e){
System.out.println("catch");
// throw new RuntimeException("catch抛出异常!!!");
return 2;
}
finally{
System.out.println("finally");
return 3; //禁止在finally中使用return语句,这里只是举例说明,在实际开发中禁止使用
}
}
复制代码
上述代码的返回的结果依旧是3。
public int test8(){
// try中没有异常发生
try{
int a=1/1;
throw new Exception("try中的异常");
}
catch(Exception e){
System.out.println("catch");
return 2;
}
finally{
System.out.println("finally");
return 3; //禁止在finally中使用return语句,这里只是举例说明,在实际开发中禁止使用
}
}
复制代码
上述代码的运行结果如下图所示:
try中没有发生异常的代码,但try中抛出了异常,那么在抛出try中的异常之前,会先去匹配catch声明的异常对象并执行catch中的代码,因为catch中有return语句,那么在执行catch中的return语句之前,会先去执行finally中的代码,因为finally代码块中有return语句,所以当finally中的return语句执行完毕后,整个方法就结束了。前面的return语句就不会被执行了。
综合上面的几段代码,我们来总结一下:
当try、catch以及finally中都抛出了异常时,只有finally中的异常会被抛出,try和catch中的异常会被屏蔽。
当try、catch以及finally中都有return语句时,只有finally中的return语句会被执行,try和catch中的return语句不会被执行。
return和抛出异常(throw new XXXException())是不能同时出现在 同一个 代码块中的。
如果try中有return(throw)语句并且try中没有异常,那么在执行try中的return(throw)语句之前,要先去执行finally中的语句;同理,如果catch中有return(throw)语句,那么在执行catch中的return(throw)语句之前,要先去执行finally中的代码。
一旦执行了return语句(或throw抛出异常代码),那么该方法就立即结束了。
如果,在try、catch以及finally中混合了return语句和抛出异常代码(throw),执行原理请参照第4条结论。
一句话总结一下:如果try、catch和finally中都有return语句(或者都抛出了异常),那么只有finally中的return语句会执行(或者只有finally中的异常会被抛出),try和catch中的return语句不会被执行(或者try和catch中的异常不会被抛出)。
自定义异常类
自定义异常类必须是Throwable类的子类,通常是从Exception及其子类来继承。例如,下述的MyException类就是一个自定义类。
public class MyException extends Exception {
}
复制代码
最后
以上就是无情耳机为你收集整理的java异常处理机制方法,Java异常处理机制的全部内容,希望文章能够帮你解决java异常处理机制方法,Java异常处理机制所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复