注:网页版右上方的悬浮框有目录索引
一、异常?
程序的运行过程中所发生的不正常事件,如所需文件找不到、网络连接不能或连接中断、算术运算出错( 如被零除 )、数组下标越界、装载一个不存在的类、对 null 对象操作、类型转换异常等。异常会中断正在运行的程序。
二、Java 异常体系结构
- 所有异常都是 Throwable 类的子类,它派生了两个类:Error 类和 Exception 类。
2.1】Error 类:
- 表示仅靠程序本身无法恢复的严重错误
- 如内存溢出、动态链接失败、虚拟机错误。
- 应用程序不应该抛出这种类型的错误( 一般由虚拟机抛出 )。
- 假如出现这种错误,应尽力使程序安全退出。
2.2】Exception 类:
- 由 Java 应用程序抛出和处理的非严重错误
- 如所需文件找不到、网络连接不通或连接中断、算术运算出错( 如被零除 )、数组下标越界、装载一个不存在的类、对 null 对象操作、类型转换异常等。
- 它的各种不同的子类分别对应不同类型的异常。
2.3】Exception 可分为两大类异常
- 运行时异常
-
- 包括 RuntimeException 及其所有子类。不要求程序必须对它们进行处理。
- Checked 异常( 普通异常 )
-
- 除了运行时异常外的其他从 Exception 类继承来的异常类。
注:在进行程序设计时,应该更关注 Exception 类
三、常见的异常类
异常 | 说明 |
---|---|
Exception | 异常层次结构的根类 |
ArithmeticException | 算术错误异常,如以零作为除数 |
ArrayIndexOutOfBoundsException | 数组下标越界 |
NullPointerException | 尝试访问 null 对象成员 |
ClassNotFoundException | 不能加载所需的类 |
InputMismatchException | 欲得到的数据类型与实际输入的类型不匹配 |
IllegalArgumentException | 方法接收到非法参数 |
ClassCastException | 对象强制类型转换出错 |
NumberFormatException | 数字格式转换异常,如把“abc”转换成数字 |
四、异常处理机制
- Java 的异常处理是通过 5 个关键字来实现的,即 try、catch、finally、throw 和 throws。
4.1】使用 try-catc 处理异常
try {
Scanner in = new Scanner(System.in);
System.out.print("请输入被除数:");
int num1 = in.nextInt();
System.out.print("请输入除数:");
int num2 = in.nextInt();
System.out.println(num1+"/"+ num2 +"="+ num1/ num2);
}catch (InputMismatchException e) {
System.out.println("输入不匹配异常.....");
e.printStackTrace();
}catch (ArithmeticException e) {
System.out.println("算术异常........");
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}
4.1.1 三种情况
- 如果 try 语句块中所有语句正常执行完毕,没有发生异常,那么 catch 语句块中的所有语句都将会被忽略。
- 如果 try 语句块在执行过程中发生异常与 catch 语句块中声明的异常类型匹配,那么 try 语句块中剩下的代码都将被忽略,而相应的 catch 语句块将会被执行。
- 如果 try 语句块在执行过程中发生异常,而抛出的异常在 catch 语句块中没有被声明,那么方法立刻退出
注:在 catch 语句中可以加入用户自定义处理信息,也可以调用异常对象的方法输出异常信息
4.1.2 异常对象的方法
- void printStackTrace()
-
- 输出异常的堆栈信息。堆栈信息包括程序运行到当前类的执行流程,它将输出从方法调用处到异常抛出处的方法调用实例。
- String getMessage()
-
- 返回异常信息描述字符串,该字符串描述了异常产生的原因,是 printStackTrace() 输出信息的一部分。
4.1.3 小结
- 如果 try 语句块在执行过程中发生异常,try 语句块中剩下的代码都将被忽略,系统会自动生成相应的异常对象,包括异常的类型、异常出现时程序的运行状态及对该异常的详细描述。如果这个异常对象与 catch 语句块中声明的异常类型匹配,会把该异常对象赋给 catch 对象后面的异常参数,相应的 catch 语句块将会被执行。
4.2】使用 try-catch-finally 处理异常
4.2.1 大致分为两种情况
- 如果 try 语句块中所有语句正常执行完毕,finally 语句块也会被执行。
- 如果 try 语句块在执行过程中发生异常,无论这种异常能否被 catch 语句块捕获到,都将执行 finally 语句块中的代码。
- 特别注意
-
- 即使在 catch 语句中存在 return 语句,finally 语句块中的语句也会执行。发生异常的执行顺序是,先执行 catch 语句块中 return 之前的语句,再执行 finally 语句块中的语句,最后执行 catch 语句块中的 return 语句退出。
-
- finally 语句块中语句不执行的唯一情况是在异常处理代码 中执行了 System.exit(1) 退出Java 虚拟机。
注:try-catch-finaaly 结构中 try 语句是必须存在的, catch、finally 语句块为可选,但两者至少出现其中之一
4.3】使用多重 catch 处理异常
- 一段代码可能会引发多种类型的异常,这时,可以在一个 try 语句块后面跟多个 catch 语句块分别处理不同的异常。但排列顺序必须是从子类到父类,最后一个一般都是 Exception 类。因为按照匹配原则,如果把父类异常放到前面,后面的 catch 语句块将不会获得执行机会。
- 运行时,系统从上到下分别对每个 catch 语句块处理的异常类型进行检测,并执行第一个与异常类型匹配的 catch 语句。执行其中的一条 catch 语句之后,其后的 catch 语句将被忽略。
4.4】使用 throws 声明抛出异常
- 如果在一个方法体中抛出了异常,并希望调用者能够及时地捕获异常,Java 语言中通过关键字 throws 声明某个方法可能抛出的各种异常以通知调用者。throws 可以同时声明多个异常之间由逗号隔开。
抛出异常后的处理方式
- 过 try-catch 捕获并处理异常
- 通过 throws 继续声明异常。如果调用者不知道如何处理该异常,可以继续通过 throws 声明异常,让上一级调用者处理异常。main() 方法声明的异常将由 Java 虚拟机来处理。
4.5】使用 throws 抛出异常
- 除了系统自动抛出异常外,在编程过程中,有些问题是系统无法自动发现并解决的,如年龄不在正常范围内,性别输入不合法,此时需要程序员而不是系统来自行抛出异常,并把问题交给调用者去解决。
throws 与 throw 的区别( 如下所示 )
- 作用不同:throw 用于程序员自行产生并抛出异常,throws 用于声明该方法内抛出了异常。
- 使用的位置不同:throw 位于方法体内部,可以作为单独语句使用;throws 必须跟在方法参数列表的后面,不能单独使用。
- 内容不同:throw 抛出一个异常对象,只能是一个;throws 后面跟异常类,可以跟多个
4.6】自定义异常
- 当 JDK 中的异常类型不能满足程序的需要时,可以自定义异常类。使用自定义异常一般有如下几个步骤。
- 定义异常类,并继承 Exception 或者 RuntimeException;
- 编写异常类的构造方法,并继承父类的实现,常见的构造方法有如下 4 种形式。
public Test() {
}
public Test(String message) {
super(message);
}
public Test(String message, Throwable cause) {
super(message, cause);
}
public Test(Throwable cause) {
super(cause);
}
- 实例化自定义异常对象,并在程序中使用 throw 抛出。
4.7】异常链
- 在异常处理时有时会遇到如下情况:A 方法调用 B 方法,B 方法抛出了异常。那么 A 方法是继续招聘原有的异常还是抛出一个新异常呢?若抛出原有的异常将是很糟糕的设计方法。因为 A 方法与 B 方法进行了关联,不便于代码的修改和扩展。若抛出新的异常,虽然解决了 A 方法和 B 方法的关联问题,但是原有的异常信息却会丢失。幸运的是,JDK 1.4 推出了异常链,正好解决了这个问题。它虽然创建了新的异常,但却保留了原有异常的信息。