异常处理


异常处理

在程序运行过程中,经常会出现一些意外的情况,这些意外会导致程序出错或者崩溃,从而影响程序的正常执行,如果不能很好地处理这些意外情况,程序的稳定性就会受到影响。在 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,但继承了 Exception 或 Throwable,就称此类为检查时异常。

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 异常处理机制对这个问题的处理是,当 try 或 catch 语句块中有 return 语句时,先执行 try 或 catch 语句块中 return 语句之前的代码,再执行 finally 语句块中的代码,之后再返回。所以,即使在 try 或 catch 语句块中有 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. 常见的异常类型有 NullPointerException、ClassNotFoundException、IllegalArgumentException、InputMismatchException、IllegalAccessException、ClassCastException、SQLException 和 IOException 等,并且随着学习的深入,大家也会接触更多类型的异常。

img


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