异常处理
异常概述
java是采用面向对象的方式来处理异常的。当程序出现问题时,就会创建异常类对象并抛出异常相关的信息(如异常出现的位置、原因等)。
异常分类
- 所有异常对象都是派生于
Throwable类的一个实例。- Thorwable类(表示可抛出)是所有异常和错误的超类,两个直接子类为
Error和Exception,分别表示错误和异常。
Throwable
Throwable类是所有异常或错误的超类,它有两个子类:Error和Exception,分别表示错误和异常。其中异常Exception又分为运行时异常(RuntimeException)和编译时异常。- Error和运行时异常,因为程序在编译时不检查异常,所以又称为不检查异常(Unchecked Exception)。
- 编译时异常,因为程序在编译时,异常可以被检查出,所以又称为可检查异常(Checked Exception)。
- Throwable常用方法:
public String getMessage():返回此throwable的详细消息字符串。public String toString():返回此 throwable 的简短描述。public void printStackTrace():打印异常的堆栈的跟踪信息。
Error
- Error类是java所有错误类的父类,描述了java运行时系统内部错误和资源耗尽错误。
- 这类错误是我们无法控制的,同时也是非常罕见的错误,表明系统JVM已经处于不可恢复的崩溃状态中。它是由JVM产生和抛出的,比如
OutOfMemoryError、ThreadDeath等。- 错误是很难处理的,一般的开发人员是无法处理这些错误的,我们在编程中,可以不去处理这类错误。
Exception
- Exception类是所有异常类的父类,其子类对应了各种各样可能出现的异常事件。
- Exception是程序本身可以处理的异常,在程序中应当尽可能去处理这些异常。
- Exception又分为两大类:
UncheckedException未检查异常(运行时异常)CheckedException检查异常(编译时异常)
运行时异常
- RuntimeException及其子类异常,都属于运行时期异常。例如:ClassCastException、NullPointerException、IndexOutOfBoundsException、ArithmeticException等。
- 因为程序编译时异常不能被检查出,所以又称为不检查异常(UnCheckedException)。
- 运行时异常一般是由程序逻辑错误引起的,所以在编写程序时,我们应该从逻辑角度尽可能避免这类异常的发生。
- 当出现RuntimeException的时候,系统将自动检测并将它们交给缺省的异常处理程序(虚拟机接管并处理),用户可以不必对其处理。
编译时异常
- Exception及其子类(不包含运行异常),统称为编译时异常。
- 因为程序编译时异常可以被检查出,所以又称为可检查异常(CheckedException)。
- 常见的编译时异常:如IOException、SQLException等以及用户继承于Exception的自定义异常。
- 对于编译时异常,在编译时就强制要求我们必须对出现的这些异常进行处理,否则程序就不能编译通过。所以,面对这种异常不管我们是否愿意,都只能自己去处理可能出现的异常。
异常处理
抛出异常(throw)
- 在java语言中,提供了一个throw关键字,它用来抛出一个指定的异常对象。
- 在方法体中,使用throw关键字来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。
声明异常(throws)
- 声明:将问题标识出来,报告给调用者。如果方法内通过throw 抛出了编译时异常,而没有捕获处理,那么必须通过throws关键字进行声明,让调用者去处理。
- throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开。
- 当程序中出现运行时异常时,方法定义中无需 throws 声明该异常,调用者也无需捕获处理该异常(即没有try-catch),系统会把异常一直默认往上层抛,一直抛到最上层。
- 当程序中出现非运行时异常时,要么用try-catch语句捕获它,要么用throws语句声明抛出它,否则编译不会通过。
捕获异常(try-catch-finally)
- 如果程序出现了异常,自己又解决不了,那么可以把异常声明出来(throws),报告给调用者,让调用者来做处理。
- 如果出现的异常自己能解决,那么就不用声明异常,而是自己捕获该异常(try-catch-finally),并在catch块中对该异常做处理。
- 一个try语句必须带有至少一个catch语句块或一个finally语句块 。
try-catch组合方式
- try-catch 组合:对代码进行异常检测,并对检测到的异常传递给 catch 处理。
- 一个try多个catch组合 : 对代码进行异常检测,并对检测的异常传递给catch处理。对每种异常信息进行不同的捕获处理。
- 这种异常处理方式,要求多个 catch 中的异常不能相同,并且若 catch 中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的 catch 处理,父类异常在下面的 catch 处理。
try-catch-finally组合方式
- try-catch-finally组合:检测异常,并把捕捉到的异常传递给catch代码块处理,然后在finally中进行资源释放。
- 有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而 finally 就是解决这个问题的,在 finally 代码块中存放的代码都是一定会被执行的。
- 即使在try或catch中添加return,finally中的代码都会执行,除非调用System.exit(0);做退出虚拟机的操作,这样finally中的代码才不会执行。
- finally中添加return,则使用finally中的返回值。
try-finally组合方式
对代码进行异常检测,检测到异常后因为没有catch,所以一样会被默认 jvm 抛出。异常是没有捕获处理的。但是所开启的资源需要进行关闭,所有finally只为关闭资源。
自定义异常
- java中所有的异常类,都是继承Throwable,或者继承Throwable的子类。这样该异常才可以被throw抛出。
- 继承Exception属于非运行时异常,如果没有对其异常进行捕获处理(try-catch),那么必须在方法上声明异常(throws),以便告知调用者进行捕获。
- 继承RuntimeException属于运行时异常,方法上不需要声明异常(throws ),调用者也可以不捕获异常(try-catch)。代码一旦抛出异常,那么程序就会挂掉,并有JVM把异常信息显示到日志窗口上,让调用者看到异常并修改代码。
自定义异常的规范
- **命名规范:**自定义异常类名应以
Exception结尾(如InvalidParameterException),以明确标识异常类型。- 继承关系:
- 检查性异常(需强制处理)继承
Exception类。- 非检查性异常(运行时异常)继承
RuntimeException类。- 避免直接继承
Throwable或Error。- 序列化支持:若异常需跨网络传输或持久化,需实现
Serializable接口,并显式声明serialVersionUID- 构造方法规范:
- 空参构造方法:
public CustomException() {}- 带错误信息的构造方法:
public CustomException(String message)- 带错误信息和原因的构造方法:
public CustomException(String message, Throwable cause)- 封装额外信息:可添加自定义字段(如错误码、业务参数),便于定位问题。
方法重写中的异常
- 子类声明的异常范围不能超过父类声明范围。
- 子类重写父类方法时,不能抛出比父类更多的受检异常,可以选择不抛出异常或抛出更具体的异常。
- 子类在重写父类方法时,可以抛出新的运行时异常。
