异常处理


异常处理

在程序运行过程中,经常会出现一些意外的情况,这些意外会导致程序出错或者崩溃,从而影响程序的正常执行,如果不能很好地处理这些意外情况,程序的稳定性就会受到影响。在 Java 语言中,将这些程序意外称为异常(Exception),出现异常时的处理称为异常处理,合理的异常处理可以使整个项目更加稳定,也可以分离程序中的正常逻辑代码和异常处理逻辑代码,便于代码的阅读和维护。

异常种类

image-20211122190636235

  • Throwable:异常的基类,所有异常都继承自 java.lang.Throwable 类,Throwable 类有两个直接子类:Error 类和 Exception 类。

  • Error:是 Java 应用程序本身无法恢复的严重错误,应用程序不需要捕获、处理这些严重错误。通常情况下,程序员无需处理此类异常。

  • Exception:由 Java 应用程序抛出和处理的非严重错误(即异常),也是我们本章重点学习的对象。异常可分为运行时异常(RuntimeException)和检查时异常(CheckedException)两种。

  • RuntimeException:运行时异常,即程序运行时抛出的异常,程序员在编程时即使不做任何处理,程序也能通过编译。前面数组下标越界异常和除数为 0 的异常都是运行时异常。

  • CheckedException:检查时异常,又称为非运行时异常,这样的异常要求程序员必须在编程时进行处理,否则就会编译不通过。需要特别注意的是,在 JDK 的异常定义体系中(即在所有 Throwable 的子类中),并不存在真正的 CheckedException 类。也就是说,上图中的所有类名,都能在 JDK 中找到对应的 API,但唯独 CheckedException 类并不是真实存在的。一般而言,如果一个类继承自 RuntimeException,就称此类为运行时异常;反之,如果一个类没有继承 RuntimeException,但继承了 ExceptionThrowable,就称此类为检查时异常。

try catch语句

try catch

  1. 捕获异常并处理:先在一段可能抛出异常的代码外,用 try{...}catch{...} 结构包裹起来。如果运行时发生了异常,那么此次异常就会进入 catch{...} 代码块;如果一直没有异常发生,程序就不会进入 catch{...} 代码块。并且 catch{...} 可以针对抛出的不同类型的异常捕获后进行分类处理。
  2. 抛出异常:通过 throws 关键字,将异常抛给上一级处理。

try { //可能抛出异常的语句块 }

catch(SomeException1 e) { // SomeException1 特指某些异常,非 Java 中具体异常,下同 //当捕获到 SomeException1 类型的异常时执行的语句块 }

catch(SomeException2 e) { //当捕获到 SomeException2 类型的异常时执行的语句块 }

finally { //无论是否发生异常都会执行的代码 }

所有的catch结构都是平行结构,类似于switch语句中的case:结构,只要符合条件,无论catch在哪一级都会触发并执行相关语句所以会出现

finally 语句

使用 finally 语句块,保证了不论 try 语句块中是否出现异常,finally 语句块中的代码都会被执行

也许有人会有这样的疑问,如果在 try 语句块中或者 catch 语句块中存在 return 语句,finally 语句块中的代码还会执行吗?不是说 return 语句的作用是将结果返回给调用者,而不再执行 return 语句后面的代码吗?Java 异常处理机制对这个问题的处理是,当 trycatch 语句块中有 return 语句时,先执行 trycatch 语句块中 return 语句之前的代码,再执行 finally 语句块中的代码,之后再返回。所以,即使在 trycatch 语句块中有 return 语句,finally 语句块中的代码仍然会被执行。

在异常处理结构中,finally 语句块不执行的唯一一种情况就是如果在 catch 语句中出现 System.exit(),该方法表示关闭 JVM。

样例分析

image-20211123194821552

java.lang.ArrayIndexOutOfBoundsException:3 表示此次发生的异常类型是 ArrayIndexOutOfBoundsException ,并且是在数组下标为“3”时触发的此次异常;而 at TestEx12.main(TestEx12.java 8) 表示此次发生异常的代码是在 TestEx12 类中的 main() 方法里(TestEx12.java 文件中的第 8 行)。

常见异常

  • NullPointerException:空指针异常(程序员经常会遇到)属于运行时异常。解释为程序遇到了空指针,简单地说,就是调用了未经初始化的对象或者不存在的对象,或是访问或修改 null 对象的属性或方法。比如说,对数组操作时出现空指针,很多情况下是程序员把数组的初始化和数组元素的初始化混淆了,如果在数组元素还没有初始化的情况下调用了该数组元素,则会抛出空指针异常。

  • ClassNotFoundException:见名知义,该异常为类没能找到的异常。出现这种情况一般有三种原因:一是的确不存在该类;二是开发环境进行了调整,例如类的目录结构发生了变化,编译、运行路径发生了变化等;三是在修改类名时,没有修改调用该类的其他类,导致类找不到的情况。

  • IllegalArgumentException:抛出该异常表明向方法传递了一个不合法或不正确的参数。

  • InputMismatchException:由 Scanner 抛出,表明 Scanner 获取的内容与期望类型的模式不匹配,或者该内容超出期望类型的范围。例如需要输入的是能转换为 int 型的字符串,结果却输入了 abc,则会抛出这个异常。

  • IllegalAccessException:当应用程序试图创建一个实例、设置或获取一个属性,或者调用一个方法,但当前正在执行的方法无法访问指定类、属性、方法或构造方法的定义时,抛出 IllegalAccessException

  • ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出该异常。

  • SQLException:提供关于数据库访问错误或其他错误信息的异常。

  • IOException:当发生某种 I/O 异常时,抛出此异常。此类是失败或中断的 I/O 操作生成的异常的通用类。

throws抛出异常

throws 用于声明一个方法会抛出异常,就是当方法本身不知道或者不愿意处理某个可能抛出的异常时,可以选择用 throws 关键字将该异常提交给调用该方法的方法进行处理。声明方法抛出异常很简单,只需要在方法的参数列表之后,在方法体的大括号前,增加 throws 异常列”即可。

throws抛出的异常全部都是抛给了上一级,main最后可以抛给JVM虚拟机,同时如果类似于A抛出给B,B有抛出给C时,B类中抛出的异常必须包括A中抛出的异常,当然B中抛出的异常范围也可以比A中的大,例如抛出一个exception。

实用范例

image-20211123201611875

自定义异常

顾名思义,就是程序员自己定义的异常。当 Java 类库中的异常不能满足程序需求时,程序员可以自己定义并使用异常。下面结合一个实际的例子,介绍如何定义并使用自定义异常。

语法上,自定义异常可以任意的继承一个异常类。但 Exception 类是 Java 中所有异常类的父类所以在定义自定义异常类时,通常继承自该类。

image-20211123201733022

image-20211124203700298

message就是输出异常信息的作用,获取父类exception的相关异常信息。

throw

throw与throws并不相同,throw一般会与自定义异常一起使用,用于声明自定义异常,但重点是,使用了throw之后,必须使用try catch或者throws抛出异常(你都假设异常了,总得象征性处理一下吧)

总结

本章讲解的是异常处理机制,用于处理程序中可能发生的异常,主要涉及了以下知识点:

  1. 异常处理机制可以将程序中的业务代码和异常代码相分离,从而使得开发者们可以更加专注于编写业务代码,并使程序更加优雅。
  2. 异常的继承结构:异常的顶层基类是 Throwable;继承自 RuntimeException 的类称为运行时异常,否则称为检查异常。
  3. 运行时异常在编译阶段没有提示,只是在运行时存在异常才抛出;而检查异常是会在编译时就提示开发者必须处理的异常。
  4. 在异常可能发生的地方,通常有两种预防性处理策略:如果开发者认为此时出现的异常最好立即进行处理,那么就可以采用 try...catch 的方式;反之,如果开发者认为此时出现的异常,暂时可以不进行处理,就可以通过 throws 将异常抛出给上一级去处理。
  5. 异常对象常用方法有两个:一个方法是 printStackTrace(),用于输出异常的堆栈信息;另一个方法是 getMessage(),用于返回异常详细信息字符串。
  6. 异常的 5 个关键字:try 中编写可能存在异常的代码,catch 用于捕获异常并书写异常处理代码,finally 中的代码无论是否出现异常都会被执行(此前有 System.exit()除外),throws 用于声明方法可能会抛出的异常,而 throw 表示手工抛出异常即制造异常并抛出。
  7. 自定义异常:如果 JDK 中已有的异常不能满足开发需求,就需要开发者自定义异常。自定义异常需要继承 JDK 中已有的异常,并且通常会结合 if 语句一起使用。
  8. 常见的异常类型有 NullPointerExceptionClassNotFoundExceptionIllegalArgumentExceptionInputMismatchExceptionIllegalAccessExceptionClassCastExceptionSQLExceptionIOException 等,并且随着学习的深入,大家也会接触更多类型的异常。

img


文章作者: nightingale
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 nightingale !
  目录