Java中的异常

Java中的异常


初识异常
异常是指在程序的运行过程中发生的不正常事件,比如所需要的文件找不到、数据源无法连接、网络连接不通或连接中断、算术被零除运算错误、数组下标越界、空指针异常,类型转换异常等。
下面通过下面的代码来认识程序中的异常:
Java中的异常
代码分析:
这段代码中,用户输入2个数字,除数不能为零,正常情况下,运行结果如下:
Java中的异常

但是,如果用户没有按照要求进行输入,如果被除数没有输入数字,而是输入了“abc”,则程序将会发生异常,运行结果如下:
Java中的异常
输出告诉我们:在ExTest类的main函数中,出现了输入格式不匹配(例如要数字但实际输入不是数字)异常(java.util.InputMismatchException)。

若除数输入为“0”,则程序运行时也将发生异常,运行结果如下:
Java中的异常
输出告诉我们:在ExTest类的main函数中,出现了算术错误(被除数被零除)异常(java.lang.ArithmeticException)。

以上代码如果通过if-else语句进行异常处理,有以下缺点。

  • 代码臃肿,增加了大量处理异常的代码。
  • 浪费时间,有一些时间分散到异常处理上,影响开放效率。
  • 很难穷举所有的异常,程序不健壮。
  • 异常处理代码和业务代码交织在一起,影响可读性,增加维护难度。

Java提供了专门的异常处理机制,刻服了以上通过if-else来解决的异常的缺点。

异常类
Java中的异常有很多类型,所有类型都有一个共同的父类Throwable。以Throwable为根,Java定义了非常多的异常类,表示各种类型的异常。
Java中的异常
Throwable是所有异常的基类,它有两个子类:Error和Exception。

  1. Error类:表示系统错误或资源不足,如内存溢出、虚拟机错误。由Java系统使用,应用程序不应该抛出这种类型的错误。
  2. Exception类:表示应用程序错误,如算术被零除运算错误、数组下标越界、空指针异常等。Exception又可分为两大类异常。

运行时异常:包括RuntimeException及其所有子类。不要求程序必须对它们进行处理。
编译(checked)异常(非运行时异常)除了运行时异常外的其他从Exception类继承来的异常类。

以下为一些常见的异常类。

  • NullPointerException
    空指针异常,操作一个 null 对象的方法或属性时会抛出这个异常。

  • OutofOutofMemoryError
    内存出现异常的一种异常,这不是程序能控制的。

  • IOException
    在读写磁盘文件、网络内容的时候经常会生的一种异常,这种异常是受检查异常,需要进行手工捕获。

  • FileNotFoundException
    文件找不到异常,如果文件不存在就会抛出这种异常。

  • ClassNotFoundException
    类找不到异常,这是在加载类的时候抛出来的,即在类路径下不能加载指定的类。

  • ClassCastException
    类转换异常,将一个不是该类的实例转换成这个类就会抛出这个异常。

  • NoSuchMethodException
    没有这个方法异常。

  • ArrayIndexOutOfBoundsException
    数组下标越界异常。

  • ArithmeticException
    算术异常,发生在数字的算术运算时的异常,如一个数字除以 0 就会报这个错。

  • SQLException
    SQL异常,操作数据库时发生的异常。

异常处理机制

  • 使用try-catch处理异常

代码如下:
Java中的异常
这段代码中,我们使用try/catch捕获异常,如果用户按照要求输入正确,程序执行完try语句块的代码,没有发生异常,则catch语句块中的代码被忽略。如果在try语句块中发生异常,那么造成异常的那一行剩下的代码都将被忽略,而相应的catch语句块将会被执行。

如果用户在控制台中输入了被除数为“abc”,运行结果如下:
Java中的异常

如果用户在控制台中输入了除数为“0”,运行结果如下:
Java中的异常

  • 使用try-catch-finally处理异常
    代码如下:
    Java中的异常
    try-catch-finally语句块的执行流程大致分为如下两种情况:如果在try语句块中没有发生异常,finally语句块也会被执行。如果try语句块在执行过程中发生异常,无论这种异常能否被catch语句块捕获到,都将执行fianlly语句中的代码。即使在catch语句中存在return语句,finally语句块中的语句也会执行。发生异常时的执行顺序是,先执行catch语句块中return之前的语句,再执行finally语句块中的语句,最后执行catch语句块中的return语句退出。运行结果如下:
    Java中的异常

  • 使用多重catch处理异常
    代码如下:
    Java中的异常
    这个程序已知在运行时可能会引发多个异常,这时可以在一个try语句块后面跟多个catch语句块分别处理不同的异常。排列顺序必须是从子类到父类,最后一个一般都是Excteption类。

  • 使用throws抛出异常
    Java中用throws声明某个方法可能抛出的各种异常以通知方法调用者。throws可以同时声明多个异常,异常之间用逗号隔开。
    代码如下:
    Java中的异常

  • 使用throw抛出异常
    代码如下:
    Java中的异常
    运行结果如下:
    Java中的异常

这里注意一点,throw 和 throws的区别:
a.作用不同:throw用于程序员自行产生并抛出异常,throws用于声明该方法内抛出异常。
b.使用的位置不同:throw位于方法体内部,可以作为单独语句使用;throws必须跟在方法参数列表的后面,不能单独使用。
c.内容不同:throw抛出一个异常对象,是能是一个;throws后面跟异常类,可以跟多个。

  • 自定义异常
    除了Java API中的异常类型,我们可以自定义异常类。自定义异常类,方法是继承Exception或者RuntimeException。代码如下:
    MyException类
    Java中的异常
    Person类
    Java中的异常
    AppExTest类
    Java中的异常
    运行结果如下:
    Java中的异常

  • 异常链
    在异常处理时,常常会在捕获一个异常后抛出另外一个异常,并且希望把异常原始信息保存下来,这被称为异常链。在JDK1.4以前,程序员必须自己编写代码来保存原始异常信息。JDK1.4推出以后,正好解决了这个问题,它虽然创建了新的异常,但却保留了原有异常的信息。例如:
    Java中的异常
    运行结果如下:
    Java中的异常