一、异常?
程序的运行过程中所发生的不正常事件,如所需文件找不到、网络连接不能或连接中断、算术运算出错( 如被零除 )、数组下标越界、装载一个不存在的类、对 null 对象操作、类型转换异常等。异常会中断正在运行的程序。
二、Java 异常体系结构
所有异常都是 Throwable 类的子类,它派生了两个类:Error 类和 Exception 类。
注:在进行程序设计时,应该更关注 Exception 类
2.1 Error 类:
表示仅靠程序本身无法恢复的严重错误,如内存溢出、动态链接失败、虚拟机错误。应用程序不应该抛出这种类型的错误( 一般由虚拟机抛出 )。假如出现这种错误,应尽力使程序安全退出。
2.2 Exception 类:
由 Java 应用程序抛出和处理的非严重错误,如所需文件找不到、网络连接不通或连接中断、算术运算出错( 如被零除 )、数组下标越界、装载一个不存在的类、对 null 对象操作、类型转换异常等。它的各种不同的子类分别对应不同类型的异常。
2.3 Exception 可分为两大类异常
运行时异常:包括 RuntimeException 及其所有子类。不要求程序必须对它们进行处理。
Checked 异常( 非运行时异常 ):除了运行时异常外的其他从 Exception 类继承来的异常类。
三、常见的异常类
异常 | 说明 |
Exception | 异常层次结构的根类 |
ArithmeticException | 算术错误异常,如以零作为除数 |
ArrayIndexOutOfBoundsException | 数组下标越界 |
NullPointerException | 尝试访问 null 对象成员 |
ClassNotFoundException | 不能加载所需的类 |
InputMismatchException | 欲得到的数据类型与实际输入的类型不匹配 |
IllegalArgumentException | 方法接收到非法参数 |
ClassCastException | 对象强制类型转换出错 |
NumberFormatException | 数字格式转换异常,如把“abc”转换成数字 |
四、异常处理机制
Java 的异常处理是通过 5 个关键字来实现的,即 try、catch、finally、throw 和 throws。
4.1 使用 try-catc 处理异常
4.1.2 三种情况
1、如果 try 语句块中所有语句正常执行完毕,没有发生异常,那么 catch 语句块中的所有语句都将会被忽略。
2、如果 try 语句块在执行过程中发生异常与 catch 语句块中声明的异常类型匹配,那么 try 语句块中剩下的代码都将被忽略,而相应的 catch 语句块将会被执行。
3、如果 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 大致分为两种情况
1、如果 try 语句块中所有语句正常执行完毕,finally 语句块也会被执行。
2、如果 try 语句块在执行过程中发生异常,无论这种异常能否被 catch 语句块捕获到,都将执行 finally 语句块中的代码。
4.2.2 特别注意
即使在 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 可以同时声明多个异常之间由逗号隔开。
4.4.1 抛出异常后的处理方式
1、过 try-catch 捕获并处理异常
2、通过 throws 继续声明异常。如果调用者不知道如何处理该异常,可以继续通过 throws 声明异常,让上一级调用者处理异常。main() 方法声明的异常将由 Java 虚拟机来处理。
4.5 使用 throws 抛出异常
除了系统自动抛出异常外,在编程过程中,有些问题是系统无法自动发现并解决的,如年龄不在正常范围内,性别输入不合法,此时需要程序员而不是系统来自行抛出异常,并把问题交给调用者去解决。
注:throws 与 throw 的区别( 如下所示 )
1、作用不同:throw 用于程序员自行产生并抛出异常,throws 用于声明该方法内抛出了异常。
2、使用的位置不同:throw 位于方法体内部,可以作为单独语句使用;throws 必须跟在方法参数列表的后面,不能单独使用。
3、内容不同:throw 抛出一个异常对象,只能是一个;throws 后面跟异常类,可以跟多个
4.6 自定义异常
当 JDK 中的异常类型不能满足程序的需要时,可以自定义异常类。使用自定义异常一般有如下几个步骤。
1、定义异常类,并继承 Exception 或者 RuntimeException;
2、编写异常类的构造方法,并继承父类的实现,常见的构造方法有如下 4 种形式。
3、实例化自定义异常对象,并在程序中使用 throw 抛出。
4.7 异常链
在异常处理时有时会遇到如下情况:A 方法调用 B 方法,B 方法抛出了异常。那么 A 方法是继续招聘原有的异常还是抛出一个新异常呢?若抛出原有的异常将是很糟糕的设计方法。因为 A 方法与 B 方法进行了关联,不便于代码的修改和扩展。若抛出新的异常,虽然解决了 A 方法和 B 方法的关联问题,但是原有的异常信息却会丢失。幸运的是,JDK 1.4 推出了异常链,正好解决了这个问题。它虽然创建了新的异常,但却保留了原有异常的信息。