0
点赞
收藏
分享

微信扫一扫

第七章《Java的异常处理》第2节:异常的分类及处理方法

​异常可以分为多种类型,Java语言允许程序员使用不同的方式来处理不同种类的异常,这样可以实现对异常的精细化处理。

7.2.1异常的分类

7.1小节中提到Exception是用来表示异常的类,但Exception并非Java语言中唯一用来表示异常的类,它只是庞大的异常类家族中的一员。下图7-7就是Java异常类“家族图谱”的一部分。​

第七章《Java的异常处理》第2节:异常的分类及处理方法_异常处理

图7-7 异常类的继承结构​

通过图7-7可以看出,异常类家族的祖先是Throwable,它有两个重要的子类分别是Exception和Error,并且Exception和Error也各自有众多的子类。在Java语言中,Exception和Error用来表示不同的运行时错误。​

Exception通常被翻译成“异常”,它用来表示程序中可预测并且可恢复的运行时错误,这种运行时错误的严重程度不高,完全可以通过程序控制它所带来的危害。在实际开发过程中,程序员要通过代码监控异常的产生并尽量对其做出合理的处理。​

Error通常被翻译成“错误”,它表示程序运行过程中出现的比较严重的问题,这些严重问题一般都会导致程序或虚拟机本身处于非正常状态,并且通过程序无法恢复。一般来讲,Error的出现并不是由开发者编写代码不当引起的,例如虚拟机内存资源耗尽时,就会出现OutOfMemoryError。既然Error出现所导致的非正常状态无法通过程序恢复,所以程序员也无需专门写代码监控并处理它们。​

Exception类的子类都分别代表了某一种特定的异常。例如【例07_01】中,因数字被0整除所产生的异常就用ArithmeticException来表示,表7-1展示了常见的异常种类。​

表7-1常见的异常种类​

名称

意义

ArithmeticException

算术异常,如除以0

ArrayIndexOutOfBoundsException

数组越界异常

NullPointerException

空指针异常引用指向空对象时调用属性或方法引起

ClassNotFoundException

无法加载类异常

ClassCastException

类型转换异常

IOException

IO异常

FileNotFoundException

无法找到文件异常

在定义catch块时,都要声明该catch块用来捕获和处理哪一种异常。如果catch块中声明的异常类是父类,那么这个异常类所有子类的异常都能被它捕获。例如,在【例07_02】中catch块所声明要捕获的异常为Exception,那么无论是表示算术异常的ArithmeticException还是表示数组越界异常的ArrayIndexOutOfBoundsException都可以被这个catch块捕获,因为它们都是Exception的子类。​

7.2.2处理多种异常

当try块中代码量比较多的时候,有可能产生多种异常。如果程序员希望对每一种异常都用独特方式进行处理,那么可以为try块搭配多个catch块,每个catch块负责捕获一种特定异常并对其进行单独处理,下面的【例07_07】就展示了如何处理一段代码中的多种异常。​

【例07_07 处理多种异常】

Exam07_07.java​

import java.util.Scanner;
public class Exam07_07 {
public static void main(String[] args) {
int x;
int[] arr1 = new int[3];
int[] arr2 = new int[10];
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整数");
try {
x = sc.nextInt();//①
int a = 1/x;//②
arr1[x] = 2;//③
arr2[x] = 3;//④
}catch(ArithmeticException e) {
System.out.println("处理算术异常");
}catch(ArrayIndexOutOfBoundsException e) {
System.out.println("处理数组越界异常");
}catch(Exception e) {
System.out.println("处理其他异常");
}
}
}

【例07_07】中,try块搭配了3个catch块,这些catch块分别处理ArithmeticException、ArrayIndexOutOfBoundsException和Exception。当用户输入0时,程序运行到语句②会出现算术异常,第1个catch能够捕获到这个异常,因此程序最终输出结果为“处理算术异常”。当用户输入5时,程序运行到语句③会出现数组越界异常,第2个catch能够捕获到这个异常,程序最终的输出结果为“处理数组越界异常”。当用户输入100时,对于语句③和④而言,访问数组元素的操作都已经越界,但程序并不会执行到语句④,因为在执行到语句③时已经出现异常,此时虚拟机不会执行语句④,而是立刻跳转到第2个catch块中执行代码。当用户输入的不是数字而是其他符号,程序运行到语句①会出现异常,但第1和第2个catch都不会捕获到,因为nextInt()方法接收非整数时会产生InputMismatchException,它代表输入不匹配异常,但第3个catch会捕获到这个异常。虽然第3个catch声明它负责捕获的异常种类是Exception,但InputMismatchException是Exception的子类,所以第3个catch仍然会捕获这个异常,因此程序最终的输出结果为“处理其他异常”。​

可以看出:try块中产生异常的可能性虽然有很多,但在实际运行程序时,最多只能产生1个异常,因为一旦产生一个异常,虚拟机就会立刻跳出try块,后面可能产生异常的语句根本没有机会执行,所以也就不会再产生第2个异常。建议各位读者在运行【例07_07】时输入各种不同的整数以及其他字符来观察程序会产生何种异常。​

当异常产生时,多个并列的catch不会重复捕获异常,也就是说:前面的catch捕获异常后,后面的catch不会再次捕获异常。例如在【例07_07】中,当ArithmeticException异常产生时,第1个catch会捕获到这个异常,而第3个catch不会再次捕获到这个异常,虽然它有能力捕获这个异常,因此,即使try块搭配多个catch,最多也只会有1个catch捕获到异常。由于多个并列的catch最多仅有一个能捕获到异常,所以在排列这些catch块时,要把捕获子类异常的catch排在前面,而把捕获父类异常的catch排在后面,否则会出现语法错误,如图7-8所示。​

第七章《Java的异常处理》第2节:异常的分类及处理方法_异常处理_02

图7-8 多个catch排列不合理导致语法错误​

从图7-8可以看出:第2和第3个catch都出现了表示语法错误的红色波浪线。之所以出现这个语法错误,就是因为第1个catch负责捕获Exception,而Exception是所有异常的祖先类,因此第1个catch能够捕获所有类型的异常,这会导致第2和第3个catch根本没有机会捕获到任何异常,从而成为永远无法执行的代码块。​

在【例07_07】中,如果去掉捕获Exception的那个catch,那么当用户输入的不是整数时会产生InputMismatchException,而用于捕获ArithmeticException和ArrayIndexOutOfBoundsException的那两个catch块没有能力捕获到这个异常,因此异常会逃脱所有catch的捕获。此时,虚拟机会强行中止程序的运行。由此可以看出:并不是try只要搭配了catch就能一定捕获异常。实际开发过程中,如果程序员担心try块中出现的异常不能被捕获,可以在多个catch块的最后添加一个用于捕获Exception的catch来保证所有异常都能被捕获。​

当try块搭配了多个catch时,如果希望能在同一个catch块处理几种不同的异常,就可以在这个catch中声明多种异常。把多种异常写到同一个catch中需要用|运算符分隔每种异常。例如,希望用同一个catch处理ArithmeticException和ArrayIndexOutOfBoundsException这两种异常,就可以把它们写到同一个catch中,格式如下:​

catch(ArithmeticException | ArrayIndexOutOfBoundsException e) {

     ...

}

需要注意:把多个异常类写到同一个catch中时,不能把父类异常和子类异常写在一起,例如不能把Exception和ArithmeticException写到同一个catch中,因为父类异常的表示范围已经完全涵盖了子类异常的表示范围,此时子类异常就成为了多余。​

catch块只能捕获它所匹配的try块中产生的异常,其余异常均不能捕获。下面的【例07_08】就能展示出这个特性。​

【例07_08 catch块捕获异常的范围】

Exam07_08.java

public class Exam07_08 {
public static void main(String[] args) {
int x;
try {
x = 1/0;//①
}catch(ArithmeticException e) {
x = 1/0;//②
}catch(Exception e) {
System.out.println("捕获异常");
}
}
}

在【例07_08】中,try搭配了两个catch。执行语句①会产生算术异常,而第1个catch恰好能捕获到这个异常,所以虚拟机会进入第1个catch块中执行代码。在第1个catch块中,执行语句②时又会产生一个新的算术异常。第2个catch块虽然具有捕获算术异常的能力,但由于第2个catch只负责捕获与之匹配的try块中产生的异常,而当前产生的这个算术异常并不是来自于它所匹配的try块,因此第2个catch并不会捕获该异常。这个异常因为没有对应的catch进行捕获,所以它会逃脱所有的catch,此时虚拟机就会强行中止程序,并把这个异常的信息输出到控制台上,因此【例07_08】运行结果如图7-9所示。​

第七章《Java的异常处理》第2节:异常的分类及处理方法_异常分类_03

图7-9 【例07_08】运行结果



举报

相关推荐

0 条评论