Java中异常的概念、分类、抛异常及异常的处理

146 阅读6分钟

异常(Exception)

Java语言规范将派生于Error类或RuntimeException类的所有异常称为非受查异常,所有的其他异常称为受查异常如果一段代码可能抛出受查异常,那么必须显式进行处理

  • 编译时异常:在程序编译期间发生的异常,称为编译时异常,也称为受检查异常(Checked Exception)
  • 运行时异常: 在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常(Unchecked Exception)RunTimeException以及其子类对应的异常,都称为运行时异常。比如:NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException

注意:编译错误不等于编译时异常

注意:

编译时出现的语法性错误,不能称之为异常。例如将System.out.println 拼写错了, 写成了system.out.println. 此时编译过程中就会出错, 这是 "编译期" 出错。而运行时指的是程序已经编译通过得到class 文件了, 再由 JVM 执行过程中出现的错误

异常的体系结构

image.png

从上图可看出:

  1. Throwable:是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception

  2. Error:指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽等,典型代表:StackOverflowError和OutOfMemoryError,一旦发生回力乏术。

  3. Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行。比如:感冒、发烧。我们平时所说的异常就是Exception。

  • 由于Exception类是所有异常类的父类,因此可以用这个类型表示捕捉所有异常。(可以这样做但不建议)

  • 顶层类Throwable派生出两个重要的子类,Error和Exception

  • 其中Error指的是Java运行时内部错误和资源耗尽错误,应用程序不抛出此类异常。这种错误一旦出现,除了告知用户并使程序终止之外,在无能为力,这种情况很少出现。

  • Exception是我们程序员所使用的异常类的父类。

  • 其中Exception有一个子类称为RuntimeException,这里面又派生出很多我们常见的异常类NullpointerException,IndexOutOfBoundsException等

throws和throw

throws(异常声明)

throws的作用: 可以告诉方法的调用者可能会抛出的异常是什么我们可以使用throws关键字,把可能抛出的异常显式的标注在方法定义的位置,从而提醒调用者要注意捕获这些异常。

throws处在方法声明时参数列表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助throws将异常抛给方法的调用者来处理。即当前方法不处理异常,提醒方法的调用者处理异常

注意:

1. `throws`必须跟在方法的参数列表之后
2. 声明的异常必须是 `Exception` 或者 `Exception` 的子类
3. 方法内部如果抛出了多个异常,`throws`之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可
4. 调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用`hrows`抛出

throw(异常的抛出)

  • 在Java中,可以借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者
  • throw的作用:使用throw关键字可以手动抛出某个异常

注意:

  1. throw必须写在方法体内部
  2. 抛出的对象必须是Exception或者 Exception 的子类对象
  3. 如果抛出的是RunTimeException 或者RunTimeException 的子类,则可以不用处理,直接交给JVM来处理
  4. 如果抛出的是编译时异常,用户必须处理,否则无法通过编译
  5. 异常一旦抛出,其后的代码就不会执行

异常的基本用法

  1. try代码块中放的是可能出现异常的代码

  2. catch代码块放的是出现异常后的处理行为

  3. finally代码块中的代码用于处理善后工作,会在最后执行

  4. 其中catchfinally都可以根据情况选择加或者不加

  • 如果不处理异常,那么这个异常就会交给JVM处理,一旦交给了JVM处理,程序就会立刻终止。

try, catch

try中发生异常之后,你要在对应的catch里面捕捉对应的异常 如果你没有捕捉就会交给jvm

try的特殊用法:使用try负责回收资源 将Scanner对象在try的()中创建,就能保证try执行完毕后自动将调用Scanner的close方法

注意:

  1. catch进行类型匹配的时候,不光会匹配相同类型的异常对象,也会捕捉目标异常类型的子类对象,因此catch最好是捕获具体的异常类

  2. catch在捕捉异常的时候,从上往下最好是先写子类再写父类,不要从父类到子类

  • 异常会沿着 异常的信息调用栈进行传递

finally

在写程序时,有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源:网络连接、数据库连接、IO流等,在程序正常或者异常退出时,必须要对资源进进行回收。另外,因为异常会引发程序的跳转,可能导致有些语句执行不到,finally就是用来解决这个问题的

finally一般用作资源的关闭 finally无论有没有异常,finally中的代码一定会执行 因此尽量避免在finally当中写return

注意:

  • 因为throw了异常后,其后面的代码不会执行,从而会造成资源泄漏的问题。finally的存在很好的解决了这个问题,所以一般在finally中进行一些资源清理的扫尾工作
  • 一般不建议在finally中写return
  • 空接口:又称标志接口,代表当前这个类可以序列化

异常的执行流程

  1. 程序先执行try中的代码如果try中的代码出现异常,就会结束try中的代码,看和catch中异常类型是否匹配。

  2. 如果找到匹配的异常类型,就会执行catch中的代码

  3. 如果没有找到匹配的异常类型,就会将异常向上传递到上层调用者

  4. 无论是否找到匹配的异常类型,finally中的代码都会被执行到(在该方法结束之前执行)

  5. 如果上层调用者也没有处理的了异常,就会继续向上传递

  6. 一直到main方法也没有合适的代码处理异常,就会交给JVM来进行处理,此时程序就会异常终止

  • 异常会沿着 异常的信息调用栈进行传递

自定义异常类

什么是自定义异常类? 自定义异常类就是自己定义一个类,然后继承了某个异常类。

自定义异常类如果继承了Exception,会以受查异常的情况去处理。如果继承了RuntimeException,会以非受查异常的情况去处理。

注意:

  • 自定义异常通常会继承自 Exception 或者 RuntimeException
  • 继承自 Exception 的异常默认是受查异常
  • 继承自 RuntimeException 的异常默认是非受查异常