这里列出了一些常见的异常,不代表所有异常的真实体系~
Throwable
Java中所有异常的的父类,都继承于Throwable
Error
、Exception
是异常的两个大类
常用方法
String getMessage(); //返回Throwable简短信息
String toString(); //返回Throwable详细信息
void printStackTrace(); //jvm打印异常对象默认调用此方法(异常信息、异常原因、异常位置)
Error
- 系统性的错误。程序不能处理、捕获的异常。一般需要修改源代码才可能修复
- 常见的错误:
- JVM中堆和方法区内存溢出:
OutOfMemoryError
- JVM中虚拟机栈、本地方法栈内存溢出:
StackOverflowError
- JVM中堆和方法区内存溢出:
Exception
- 程序的异常,分为运行期异常和非运行期异常
运行期异常 RunTimeException
- Exception的一个子类
- 运行期异常不用处理默认交给JVM处理
- 常见的运行期异常
- 空指针异常:
NullPointerException
- 索引越位异常(字符串、数组、集合):
IndexOutOfBoundsException
- 空指针异常:
非运行期异常
- 程序必须处理的异常,不处理不能通过编译
- 做法:
- 捕获:
try...catch...finally
- 抛出:
throws...
- 捕获:
- 常见的非运行期异常
- IO异常:
IOException
- 文件找不到异常:
FileNotFoundException
- 类找不到异常:
ClassNotFoundException
- IO异常:
发生异常时的大致流程
- JVM根据异常产生的原因创建一个异常对象,这个异常对象包含了异常产生的内容、原因、位置
- 若方法中没有异常处理逻辑(try...catch),则把对象抛给方法调用者来处理
- 若最终抛到main方法都没有异常处理逻辑,那么这个异常最终会落到JVM上,JVM会做两件事:
- 把异常对象(内容、原因、位置)以红色的字体打印到控制台上
- JVM会终止当前执行的程序,也叫中断处理
异常相关的5个关键字
try
、catch
、finally
- 含义:捕获异常
- 格式:
try { //可能产生异常的代码 } catch(异常变量) { //异常的处理逻辑 } catch(异常变量) { //catch块至少要有一个 } finally { //逻辑处理 }
- 如果catch代码块中的异常变量存在父子关系,则可以只捕获父类异常。如果不这么做,必须把子类异常放到上面,父类异常放到下面,这样可以在子类异常中做特殊处理,否则报错。
- finally代码块不可以单独使用;常用于资源释放(io、数据库连接),这些资源占用cpu的资源,无论是否出现异常都需要资源释放。
throw
- 含义:抛出异常
- 必须写在方法内部
- 抛出的必须是Exception或Exception的子类
- 构造方法
new XXXException(String message)
仅抛出异常信息new XXXException(String message, Throwable cause)
异常信息+异常原因
throws
- 含义:声明异常
- 必须写在方法声明处
- 抛出的必须是Exception或Exception的子类
- 方法内部抛出了多个异常,那么必须throws多个异常,如果这些异常(编译期异常)有父子关系,则只需声明父类异常
- 如果父类抛出了多个异常,子类重写父类方法时,可以:
- 抛出和父类相同的异常
- 可以抛出父类异常的子类
- 可以不抛出任何异常
- 如果父类没有抛出异常,子类重写父类方法时,内部有异常,不允许声明抛出,只能内部处理
try...catch...finally代码块
- 无论是否有异常;抑或是try、catch代码块中有return语句,finally都会执行
- 如果try、catch代码块有return,finally代码块没有return。执行到return时(加入return中有运算,也会执行),先把结果保存起来,不管finally中怎么改变这个值,都不会影响这个结果,基本数据类型、引用类型都是如此。所以这时返回的结果会在finally前确定好。
- 如果finally中有return语句,永远返回finally中的结果(覆盖前面的结果)
几种组合情况:
- 情况一
try { } catch{ } finally { } return value;
- 显然按照正常顺序执行
- 情况二
try { return value1; } catch { } finally{ } return value2;
- 程序执行顺序:
- try代码块中return前的代码
- finally块逻辑
- try代码块中return语句
- 注意:try中已经return了,所以后面的return是不执行的
- 情况三
try { } catch { return value1; } finally{ } return value2;
- 程序执行顺序
- try代码块
- 有异常:
- catch代码块中return前的代码
- finally块
- catch块中的return语句。finally块后面的return不执行
- 没有异常:
- finally块
- finally块后的return,没有异常catch不执行,其中的return自然不执行
- 情况四
try { return value1; } catch { } finally { return value2; }
- 程序执行顺序:
- try代码块return之前的代码
- finally块,因为有return,提前返回,覆盖前面的结果
- 情况五
try { } catch { return value1; } finally { return value2; }
- 程序执行顺序:
- catch块return之前的代码
- finally块,因为有return,提前返回,覆盖前面的结果
- 情况六
try { return value1; } catch { return value2; } finally { return value3; }
- 程序执行顺序:
- try代码块return语句之前的代码
- 有异常:
- catch代码块中return前的代码
- finally代码块,并执行finally中的return
- 没有异常:
- finally代码块,并执行finally中的return
自定义异常类
- 一般用Exception来结尾,区分其他类说明这是一个异常类
- 自定义异常类必须继承Exception或RunTimeException
- 格式
public class XXXException extends Exception/RunTimeException {
//添加一个空参构造方法
//添加一个带异常信息的构造方法
//查看源码发现,所有的异常类都有一个带异常信息的构造方法,都会调用父类带异常信息的构造方法,让父类来处理这个异常信息
}