异常处理
在程序运行过程中,经常会出现一些意外的情况,这些意外会导致程序出错或者崩溃,从而影响程序的正常执行,如果不能很好地处理这些意外情况,程序的稳定性就会受到影响。在 Java 语言中,将这些程序意外称为异常(Exception
),出现异常时的处理称为异常处理,合理的异常处理可以使整个项目更加稳定,也可以分离程序中的正常逻辑代码和异常处理逻辑代码,便于代码的阅读和维护。
异常种类
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
- 捕获异常并处理:先在一段可能抛出异常的代码外,用
try{...}catch{...}
结构包裹起来。如果运行时发生了异常,那么此次异常就会进入catch{...}
代码块;如果一直没有异常发生,程序就不会进入catch{...}
代码块。并且catch{...}
可以针对抛出的不同类型的异常捕获后进行分类处理。 - 抛出异常:通过
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。
样例分析
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。
实用范例
自定义异常
顾名思义,就是程序员自己定义的异常。当 Java 类库中的异常不能满足程序需求时,程序员可以自己定义并使用异常。下面结合一个实际的例子,介绍如何定义并使用自定义异常。
语法上,自定义异常可以任意的继承一个异常类。但 Exception
类是 Java 中所有异常类的父类,所以在定义自定义异常类时,通常继承自该类。
message就是输出异常信息的作用,获取父类exception的相关异常信息。
throw
throw与throws并不相同,throw一般会与自定义异常一起使用,用于声明自定义异常,但重点是,使用了throw之后,必须使用try catch或者throws抛出异常(你都假设异常了,总得象征性处理一下吧)
总结
本章讲解的是异常处理机制,用于处理程序中可能发生的异常,主要涉及了以下知识点:
- 异常处理机制可以将程序中的业务代码和异常代码相分离,从而使得开发者们可以更加专注于编写业务代码,并使程序更加优雅。
- 异常的继承结构:异常的顶层基类是
Throwable
;继承自RuntimeException
的类称为运行时异常,否则称为检查异常。 - 运行时异常在编译阶段没有提示,只是在运行时存在异常才抛出;而检查异常是会在编译时就提示开发者必须处理的异常。
- 在异常可能发生的地方,通常有两种预防性处理策略:如果开发者认为此时出现的异常最好立即进行处理,那么就可以采用
try...catch
的方式;反之,如果开发者认为此时出现的异常,暂时可以不进行处理,就可以通过throws
将异常抛出给上一级去处理。 - 异常对象常用方法有两个:一个方法是
printStackTrace()
,用于输出异常的堆栈信息;另一个方法是getMessage()
,用于返回异常详细信息字符串。 - 异常的 5 个关键字:
try
中编写可能存在异常的代码,catch
用于捕获异常并书写异常处理代码,finally
中的代码无论是否出现异常都会被执行(此前有System.exit()
除外),throws
用于声明方法可能会抛出的异常,而throw
表示手工抛出异常即制造异常并抛出。 - 自定义异常:如果 JDK 中已有的异常不能满足开发需求,就需要开发者自定义异常。自定义异常需要继承 JDK 中已有的异常,并且通常会结合
if
语句一起使用。 - 常见的异常类型有
NullPointerException
、ClassNotFoundException
、IllegalArgumentException
、InputMismatchException
、IllegalAccessException
、ClassCastException
、SQLException
和IOException
等,并且随着学习的深入,大家也会接触更多类型的异常。