一、异常概述

  • 异常 :指的是程序在执行过程中,出现的非正常的情况,如果不处理最终会导致JVM的非正常停止,并不是指的语法错误
  • 正确对待异常:编写程序时,应该充分考虑到各种可能发生的异常和错误,极力预防和避免,实在无法避免的,要编写相应的代码进行异常的检测、异常消息的提示,以及异常的处理
  • 异常的抛出机制:Java中把不同的异常用不同的类表示,一旦发生某种异常,就通过创建该异常类型的对象,并且抛出。程序可以catch到这个异常对象并处理,如果无法catch到这个异常对象,那么这个异常对象将会导致程序终止

二、异常体系

1、Throwable

  • java.lang.Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类或其子类的实例时,才能通过Java虚拟机或者Java的throw 语句抛出

  • Throwable中的常用方法:

    • public void printStackTrace():打印异常的详细信息(包含了异常类型、异常原因,异常出现的位置)
    • public String getMessage():获取发生异常的原因(提示给用户的时候,就提示错误原因)

2、Error和Exception

  • Throwable有两个直接子类:java.lang.Errorjava.lang.Exception

    • Error:表示严重错误,一旦发生必须停下来查看问题并解决问题才能继续,无法仅仅通过try…catch解决的错误,例如:StackOverflowError(栈内存溢出)和OutOfMemoryError(堆内存溢出,简称OOM)
    • Exception:表示普通异常,其它因编程错误或偶然的外在因素导致的一般性问题,可以通过代码的方式检测、提示和纠正,使程序继续运行,但是只要发生也是必须处理,否则程序也会挂掉,例如:空指针访问、试图读取不存在的文件、网络连接中断、数组下标越界等

三、受检异常和非受检异常

  • 平常说的异常就是指Exception,可以将异常分为:

    • 编译时期异常(即checked异常、受检异常):在代码编译阶段,编译器就能明确警示当前代码可能发生异常,如果没有编写对应的异常处理代码,则会编译失败,从而程序无法执行。例如:FileNotFoundException(文件找不到异常)
    • 运行时期异常(即runtime异常、unchecked非受检异常):即在代码编译阶段,编译器完全不做任何检查,无论该异常是否会发生,编译器都不给出任何提示。只有等代码运行起来才能被发现。通常,这类异常是由代码编写不当引起的。例如:ArrayIndexOutOfBoundsException数组下标越界异常,ClassCastException类型转换异常。

img

四、异常的生成

  • 异常对象的生成有两种方式:

    • 由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,就会在后台自动创建一个对应异常类的实例对象并抛出
    • 手动创建,格式:throw new 异常类名(实参);
  • 如果是编译时异常类型的对象,则需要处理异常,否则编译不通过

  • 如果是运行时异常类型的对象,编译器不会提示

  • throw语句是明确抛出一个异常对象,因此它下面的代码将不会执行,如果当前方法没有try…catch处理这个异常对象,throw语句就会代替return语句提前终止当前方法的执行,并返回一个异常对象给调用者

四、异常的处理

1、捕获异常try…catch

  • try…catch基本格式
1
2
3
4
5
6
7
8
try{
可能发生xx异常的代码
}catch(异常类型1 e){
处理异常的代码1
}catch(异常类型2 e){
处理异常的代码2
}
....
  • JDK1.7之后try…catch新特性:如果多个catch分支的异常处理代码一致,支持如下写法
1
2
3
4
5
6
7
8
try{
可能发生xx异常的代码
}catch(异常类型1 | 异常类型2 e){
处理异常的代码1
}catch(异常类型3 e){
处理异常的代码2
}
....
  • 如果有多个catch分支,并且多个异常类型有父子类关系,必须保证子异常类型在上,大父异常类型在下
  • 可以使用Throwable类中的方法,在catch分支中获取异常信息

2、finally块

  • 因为程序中有一些特定的代码无论异常是否发生,都需要执行(例如,IO流的关闭,数据库连接的断开等),这样的代码通常就会放到finally块中

  • 两种形式

    • 形式一:try...catch...finally,此时无论try中是否发生异常,也无论catch是否捕获异常,也不管try和catch中是否有return语句,都一定会执行
    • 形式二:try...finally,无论try中是否发生异常,也不管try中是否有return语句,都一定会执行

当只有在try或者catch中调用退出JVM的相关方法时,才会不执行

3、转换异常处理位置throws

  • 在编写方法体的代码时,某句代码可能发生编译时异常,但是在当前方法体中可能不适合处理或无法给出合理的处理方式,就可以通过throws在方法签名中声明该方法可能会发生xx异常,需要调用者处理
  • 声明异常格式:修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{}

throws后面也可以写运行时异常类型,如果写了,唯一的区别就是调用者调用该方法后,使用try…catch结构时,IDEA可以获得更多的信息,需要添加什么catch分支

  • 对于带throws的方法重写的特殊要求

    • 不能是staticfinal修饰的方法
    • throws异常列表要求
      • 如果父类方法签名后面没有throws 编译时异常类型,那么重写方法时,方法签名后面也不能出现throws 编译时异常类型,反之,需要是父类异常类型的子类
      • 对于throws 运行时异常类型没有要求
  • throws是方法可能抛出异常的声明(表示该方法可能要抛出异常)

五、自定义异常

  • 定义格式:

    • 编译时异常类型:自定义类,并继承java.lang.Exception
    • 运行时异常类型:自定义类,并继承java.lang.RuntimeException