5.2 Java异常的捕获与处理
Java异常的捕获处理机制是由try、catch和finally关键字组成的语句实现的,由于异常是由某种不正确的操作引起的,因此,其捕获是在方法体内。
5.2.1 try-catch语句
由try和catch关键字组成的异常对象捕获语句的语法格式为:
try-catch异常处理的语句结构是针对应用程序操作语句而言的,因此,try-catch语句被应用在Java类中的方法体内,try和catch语句是成对使用的,应用程序实现的正常逻辑功能代码被安排在关键字try指定的范围,即“{}”之间,而组成这些代码的Java语句可能会产生可预见的异常,处理异常的Java语句将被安排在catch语句指定的范围,即“{}”之间,处理异常的代码是与SomeException类型的异常相关的。
在catch语句“{}”中的异常处理程序代码是与在try语句中的正常逻辑程序代码分隔开的,该部分代码针对的就是修正已经出现了SomeException类型的错误,在catch语句中也可以没有程序代码,如果没有程序代码,则对异常只捕获不做任何处理,这种情况被视为将异常(错误)忽略掉,有些异常不被修正并不影响程序的正常执行。
在Java体系中,异常是以对象的形式出现的,在catch关键字后的“()”中将指明要捕获的异常对象的类型,它是catch语句的参数,且只有一个参数,参数是以声明一个对象se为SomeException某种类型的形式出现的,某种类型指的是继承Throwable类的自定义的异常类或是Java包中定义的异常类,因此,只有Throwable类或其子类才能作为catch语句的参数。
当使用try-catch结构构成的应用程序被Java虚拟机执行时,Java虚拟机只执行try语句中的程序代码,没有异常出现时并不涉及执行catch语句中的程序代码。catch语句是捕获与之对应的try语句中的代码运行时可能发生的异常,当有异常发生时,Java虚拟机则将异常对象抛出,catch语句就是针对Java虚拟机抛出的异常对象进行捕获的,并且只捕获与SomeException类型相匹配的异常对象。如果匹配成功,则程序执行流程将发生改变,Java虚拟机中断执行try语句中的程序代码,转向执行catch语句中的处理SomeException类型异常的程序代码。
【示例5-2】 为一个数组赋值。其程序代码为:
上述程序执行时可能会出现异常,原因是数组长度a.length等于10,有10个数组单元,而数组下标的起始值是从0开始的,即数组下标从0到9共有10个数组单元,当为数组循环赋值到等于a.length数组长度(循环终止值i<=a.length)时,其数组下标已经越界,即没有a[10]这个单元,因此,出现异常,Exception异常对象e是Java虚拟机抛出的,而catch语句正好将其捕获,程序得以继续执行,并没有因为异常而终止程序继续执行。
在异常对象e中的getMessage()方法是为使用者提供异常信息的,它简单描述了异常情况,上述程序执行后的输出显示为:
try-catch异常处理的语句结构还可以由一个try多个catch关键字组成,可捕获多种异常对象,其语法格式为:
当一个try后面跟有多个catch时,当异常发生时,首先与第一个catch声明的异常类型相匹配,匹配成功则执行第一个catch语句中的程序,匹配不成功则依次匹配下面的catch语句,直到匹配成功。当多个catch语句捕获的异常类型具有继承关系时,catch语句的安排顺序是应先捕获子类异常,后捕获父类异常。
一段代码可能会出现多种不同的异常需要分别处理时,可以使用多catch结构。
【示例5-3】 下面的一段程序代码可能会产生整数被0除和数组下标越界的两种异常。其程序代码为:
上述为数组赋值操作时可能产生整数被0除和数组下标越界的两种异常。当第一次循环时由于i等于0,产生整数被0除的异常,java.lang.ArithmeticException异常类是Exception类的子类,它是描述数学运算产生的错误,通过该对象ae中的getMessage()方法可获取异常的信息,整数被0除的异常信息为“/by zero”,有异常出现时正常的操作将不被执行,在catch语句中可以重新完成没有被执行的操作。例如,将a[0]赋值为200即是对正常程序做修正处理,同样当最后一次循环时,由于i等于10,超出了数组下标的界限,产生数组下标越界的异常。java.lang.ArrayIndexOutOfBoundsException异常类也是Exception类的子类,是专门用于描述数组下标越界时产生的错误,在对象aiobe中的getMessage()方法是获取数组超出界限的数值,由于没有a[10]的数组单元,因此不对该异常做修正处理,将错误忽略掉。
ArithmeticException和ArrayIndexOutOfBoundsException异常对象ae和aiobe是Java虚拟机针对出现不同的异常抛出的,通过catch语句不同的参数匹配实现捕获不同的异常,当异常被捕获以及处理后,该程序将继续进行正常的循环赋值操作,可以不需要continue语句,没有因为异常的出现而终止程序执行,上述程序执行后的输出显示为:
另外,try-catch语句是可以嵌套使用的,根据try-catch后的大括号明确try-catch语句的作用域以及try所对应的catch,嵌套使用应该针对性质不同的操作,性质相同或相近的操作使用try和多catch语句其逻辑会更清晰。
【示例5-4】 嵌套使用try-catch语句的程序示例。
上述程序的功能是将一个字符串转换成一个整数,通过java.lang包Integer类中parseInt()方法实现数据的转换,但是要求字符串是数字组成的,否则parseInt()方法将抛出一个NumberFormatException类型的异常。在main()方法的try语句中调用transformData()方法时try-catch语句发生嵌套使用的情况,当字符串不是数字组成时,将产生两种性质不同的异常。例如,上述程序其输出结果为
5.2.2 finally语句
在Java的try-catch异常处理机制中,应用程序在无异常时执行try模块中的语句,有异常时执行catch模块中的语句,出现了分支,但有时有些程序代码无论执行时是否出现异常都需要被执行。例如网络的数据传输,需要在传输前建立连接,传输完成后需要断开连接,无论传输是否正常完成,也可能会出现异常,但是最后都需要将连接断开,以免占用计算机的各种资源。再如,当一个数据库被打开后,无论对数据库读或写操作是否正常,最后都需要将数据库关闭,finally语句就是完成该任务的。
在Java体系中,由关键字finally构成的语句是对Java异常处理机制的补充,finally语句是由finally关键字后跟的“{}”以及在大括号中的程序代码组成,finally语句需要与try(-?)catch语句配合使用,finally语句的位置是在try和所有catch语句后面,finally语句的功能是,无论Java虚拟机执行的是try语句中的程序代码还是catch语句中的程序代码,即无论在try语句中的代码是否有异常出现,最后的finally语句中的程序代码都将被执行。finally语句的使用语法格式为:
在使用try-catch语句结构完成正常和异常逻辑程序的同时,有些实现特殊功能的程序代码一定要被执行时,可将这些程序代码安排在finally语句中。finally语句可以单独与try语句配合使用,即try-finally联合使用而没有catch语句,也可以与一个try语句和多个catch语句配合使用,不过finally语句总是在最后面。
【示例5-5】 try-catch-finally联合使用的程序示例。其程序代码为:
上述程序执行时无论是否有异常出现,其finally语句中的代码总是被执行,该程序输出显示结果为:
finally语句中的程序代码不因为在try和catch语句中有return、continue、break等分支语句的存在而不被执行。当在try和catch语句中有分支语句时,Java虚拟机在转移程序执行流程之前,总是执行完finally语句中的程序代码后才执行分支操作。
【示例5-6】 try结合finally语句的程序示例。
上述程序是一个try-finally配合使用的程序示例,没有catch语句,在try模块中使用了break语句,表明程序从这里要退出for循环,finally语句是属于for循环体中的,但是,finally语句中的程序代码并没有因为break语句而不被执行,在实现break动作之前,先要完成执行finally语句中的程序代码,该程序输出显示结果为: