Java提供的异常处理机制能在Java程序发生异常时,按照程序的预先设定的异常处理逻辑,有效地处理异常,使程序继续正常执行。Java程序中的异常可以在程序语句执行时所发生,也可以是编程人员通过throw 语句手动抛出的,在Java程序中一旦有异常发生,就会有一个对应类型的异常对象,在程序代码中我们可以用try块把可能发生异常的语句块包起来,用catch去捕获发生的异常对象,并做出相应的处理。
【例6.10】在Example6_10中,实现的功能是输入两个整数,再求两整数整除的结果。
我们都知道除法中,除数不能为0,如果num2输入的值是0,则在运行程序时,会发生java.lang.ArithmeticException运行时异常,由于ArithmeticException异常是非受检异常,因此在程序中可以用try-catch处理,也可以不处理。但为了使程序能够正常结束,可以在程序中用try-catch块进行异常处理。
1.try-catch语句
Java中用try-catch语句来处理异常,try语句块用于监听可能发生异常的代码块,将可能抛出异常的语句或调用的某个可能会抛出异常的方法放在try语句块内,当try语句块中有异常发生时,抛出相应异常对象,并立即中止try语句块的执行,转向相应异常处理所对应的catch语句块执行,因此catch语句块用于捕获try语句块中发生的异常。catch块在捕获try块中抛出的异常对象时,若其后有多个catch块,则是从try语句块后第一个catch块开始从上至下依次对每一个catch后的异常对象参数进行匹配,第一次匹配上某catch块后的异常对象参数,就执行相应的catch后的代码块,进行异常处理。在执行完catch块中的语句后,再顺序执行程序中catch块后的其他代码,不再匹配其他catch后的异常对象。如果try块中发生的异常没有catch块异常参数能与之匹配,则程序执行异常结束,并在控制台打印出异常堆栈信息。其格式如下:
一个try语句块可能会发生多个异常,因此一个try块后可以跟多个catch,但多个catch后的异常对象必须是不同类型的异常对象,如果多个catch块后的异常存在父子关系,则必须把父类异常放在子类异常后面进行捕获,否则如果父类异常catch块放在子类异常catch块的前面,由于在进行异常匹配时是从上至下逐一进行匹配,从而导致后面的catch块中的子类异常无法匹配成功,从而发生编译错误。
【例6.11】计算输入的两个整数的整除结果。在输入整数时,有可能输入非数字,如字母,因此可能会发生InputMismatchException异常。另外,如果除数num2输入为0时,有可能会发生ArithmeticException异常。因此,try块所包含的那段代码有可能会发生两种异常之一,所以在try块后跟了两个catch语句块,捕获可能发生的异常。
执行情况如图6.2与图6.3所示。
图6.2 输入错误数据运行结果图
图6.3 除数num2输入为0运行结果图
2.finally语句
finally语句块不能单独使用,通常以try-catch-finally形式出现,也可以是try-finally形式。格式如下:
或
finally语句块通常用于处理善后清理工作,如在try块中打开的文件或连接的数据库及网络连接在用完后都需要关闭,这些关闭操作可以放在finally语句块中执行,因为无论是否有异常发生,及发生异常是否被catch块捕获,finally语句块都会被执行。除非在其前的try块或catch块中执行了System.exit()方法,finally块将不执行。
【例6.12】在例6.11的基础上增加了finally语句块,在finally语句块中将Scanner对象input输入流对象进行关闭,即无论try中的发生什么样的异常,或者catch块是否有异常捕获,都会执行finally语句块中的关闭操作,实现资源回收。
运行结果如图6.4~图6.6所示。
图6.4 输入非整数运行图
图6.5 除数输入0运行图
图6.6 输入正确值运行图
如果在try块中发生的某个异常没有被catch块进行捕获,则程序将直接异常退出,如果没有finally块中对打开的资源进行关闭,则可能导致资源浪费,也有可能导致数据丢失,因此我们要习惯在finally块中对在try块中打开的资源进行关闭。
当在进行异常处理时,try,catch,finally三个语句块都包含时的执行情况如下:
(1)若try块中没有异常发生,则执行完try代码块后直接执行finally代码块,再执行程序中try-catch-finally语句块之后的其他语句。
(2)若在try代码块中有异常发生,则在拋出异常语句处终止try代码块的执行,若try代码块抛出的异常对象被其后catch子句捕捉,则转而执行与之相匹配的catch代码块,之后再执行finally代码块,最后再执行程序中 try-catch-finally语句块之后的其他语句。
(3)如果try代码块中拋出的异常没有被任何catch子句捕捉到,那么将直接执行finally代码块中的语句,并把该异常传递给该方法的调用者,将异常交给调用者处理,若调用者并未处理接收到的异常,则程序异常中止,并在控制台输出异常的堆栈信息。
总之,只有在try块或catch块中调用了退出虚拟机的方法System.exit(int status),在System.exit(int status)位置处会直接退出Java程序的执行,否则无论是否有异常拋出,异常处理的finally块一定会执行。
3.try-catch-finally 语句使用说明(www.daowen.com)
(1)异常处理语法结构中,try、catch、finally三个语句块都不能单独使用,try、catch、finally使用只可以是try-catch、try-catch-finally或try-finally三种形式之一。
(2)try语句块后可以跟多个 catch 块,如果在多个catch块捕获的异常类中存在父子关系,则捕获父类异常的 catch 块必须位于捕获子类异常catch块的后面。
(3)在异常处理时,若try,catch,finally语句块都存在,则catch块必须位于try块之后,而finally块必须位于所有catch块之后,且finally语句块最多只有一个。
(4)finally与try语句块匹配的语法格式,因为没有catch块的这种情况会导致异常丢失,较少使用。
4.JAVA中try、catch、finally带return的执行顺序总结
异常处理中,try、catch、finally的是按顺序执行的。如果try中没有异常,则执行顺序为try→finally,如果try中有异常,则顺序为try→catch→finally。但是当try、catch、finally语句块中有return语句的执行时,程序是遇到return就直接返回到调用处呢,还是会继续把finally语句执行结束后再返回呢?
(1)try中有return语句,且return返回基本类型数据的执行过程。
【例6.13】阅读程序Example6_13,分析运行结果。
输出结果:
当try中包含有return语句时,且没有异常发生时,会先执行try代码块中return前的代码,然后暂时保存需要return的数据,再执行finally中的代码,最后再通过return返回之前保存的信息。所以,在Example6_13程序中返回的值是try中计算后的11,而不是finally代码块中计算出来的12。
(2)try中带有return,且通过return返回引用类型数据的执行过程。
【例6.14】阅读程序Example6_14,分析运行结果。
运行结果:
Example6_14中,当执行到try块中的return numArray;语句时,同样会先临时保存需要返回的数据信息,但返回的是数组名,即引用的值,而引用值是指向堆内存中的数组的地址。当try块中没有异常发生时,同样转向finally块去执行,在执行finally代码块时,修改了numArray[0]的值之后,再回到try块的return处,由于return 的是numArray的值,而numArray里存的不是数据本身,而是指向堆内存的数组的地址,所以在finally块中已经把numArray[0]的值做了修改,因此Example6_4的主方法得到的返回的数组的元素值与finally块中修改后的值是一致的。
总之,在try块中若没有异常发生,且没有执行System.exit(int status)方法,同时在try块中有return语句,则其后finally语句块一定会被执行,在执行完finally块后会回到try块中return处,再将执行finally语句块前的暂存数据返回。注意:如果return的是基本数据类型,则其值不会受finally代码块的影响;如若返回的是引用类型,且在finally中对该引用类型数据有修改,则返回的值是会受finally代码块的执行影响。
(3)catch中带有return的执行过程。
【例6.15】阅读Example6_15程序,分析运行结果。
运行结果:
catch中包含return与try中包含return语句的执行情况是一样的,当catch代码块捕获了try块中的异常,则会先执行catch代码块中return前的代码,然后暂时保存将要return的数据于栈中,再转向执行finally中的代码,最后再从finally代码块回到catch代码块的return位置并返回之前保存的数据。所以,Example6_15类中的catchReturn方法返回的值是try、catch中累积计算后的12,而非finally中计算后的13。
当然,在catch块中返回的值是否会受finally代码块中对返回值数据的修改的影响,还得看return的数据是基本数据类型还是引用类型,如若是基本数据类型,则像Example6_15中的运行结果一样不受影响,如若返回的是引用类型,如果在finally代码块中对将返回的引用类型的数据有修改,则finally代码块将会对catch中返回的数据有影响,但最终都会从catch块中的return处返回。
(4)finally中带有return。
【例6.16】在Example6_16中,try,catch,finally块中均包含了return语句,阅读Example6_16程序,分析在主方法中调用finallyReturn()方法所获得的返回值是从try,catch及finally块中哪段代码中的返回。
运行结果:
当finally中有return的时候,try和catch中的return会失效,在执行完finally的return之后,就不会再执行try或catch中的return语句。因此在Example6_16中,先执行了try中的num++,后发生除数为0的异常,被catch块捕获执行catch块中的num++,执行到catch块中有return语句,转向到finally块去执行,在finally块中执行了num++后,在finally块中有return语句,此时将会从finally块中返回,而不会再回到catch中去返回函数的返回值了。
注意,在finally中使用return这种写法,编译虽然可以通过,但是编译器会给予警告,因此不推荐在finally中写return,因为如果在finally里发生异常,会导致catch中的异常被覆盖。
(5)try,catch,finally代码块含有return时执行小结:
① 如果在try,catch块中没有执行到System.exit ()方法,则finally中的代码总会被执行。
② 当try、catch中有return,而finally块中没有return时。则会把finally块执行结束后再返回到try或catch中的return处返回。在return值的时候,返回值的类型若是基本数据类型,其值不会受finally代码块的执行影响,若返回的是引用类型 ,则返回值若在finally代码块中有修改,则会受到finally中代码的影响。
③ 若finally代码中有return,代码会直接在finally中return处返回,无论try及catch块中是否有return语句。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。