文章目录
异常的体系结构
Throwable:指所有错误和异常的祖先类
按照错误的严重程度分类
- Error:比较严重的错误,代码处理不了的、JVM内部资源耗尽的错误
java.lang.StackOverflowError
:栈溢出java.lang.OutOfMemoryError
:堆溢出
- Exception:错误程度小, 能够用代码进行处理
java.lang.ArithmeticException:/ by zero
: 算数异常 /0java.lang.NullPointerException
:空指针异常java.lang.ArrayIndexOutOfBoundsException
:数组越界
根据处理方式的不同分类
- 编译时异常:编译不通过(除了
RuntimeException
及其子类外的其他的异常) - 运行时异常:编译通过, 但是运行时可能会出错(
RuntimeException
及其子类)
Exception是运行时异常还是编译时异常
- Exception是编译时异常和运行时异常的父类
- 在自定义异常的时候,Exception作为编译时异常
异常处理
JVM默认处理流程
- 当我们代码在执行到,发生错误的地方。
- 一旦发生错误,jvm就会终止我们自己程序的运行,转而执行jvm自己的错误处理流程
- 在发生错误地方,收集错误信息,产生一个描述错误的对象
- 访问收集到的错误信息,将错误信息,输出到控制台窗口中
执行过程
- 如果错误产生在main方法中
- 当我们的代码执行到错误行数之前,代码是正常执行的
- 当我们的代码执行到错误行数时,JVM会终止程序的执行,抛出一个该异常信息封装成的对象
- 将该对象中的异常信息,打印到控制台上,告诉程序员发生了什么问题
- 发生错误之后的语句,都不执行了
- 如果错误产生在main方法当中的另一个方法中
- 当程序执行到该方法的错误行数时,JVM会终止程序的执行
- 向上给方法的调用者抛出一个该异常信息封装成的对象
- 一直向上抛出,直到抛给main方法,main方法最终抛给JVM
- 发生异常之前的语句正常执行,但是之后的语句都不执行了
- 当程序执行到该方法的错误行数时,JVM会终止程序的执行
- 默认处理机制仅针对运行时异常
捕获异常,自己处理
try-catch
可以使用快捷键:ctrl + alt + t
- 单分支
方式一:
try{
// 可能出现异常的代码
}catch(异常类型 对象名){
// 对异常的处理操作
}
-----------------------------------------------
方式二:
try{
// 可能出现异常的代码
}catch(异常类型1 | 异常类型2 | 异常类型3 | 对象名){
// 对异常的处理操作
}
举例:
try{
System.out.println(10/0);
}
catch(ArithmeticException | NullPointerException e){
System.out.println(”出现异常“);
}
注意:catch代码块中的代码,只有try块中的代码执行出错时,才会执行!
捕获异常信息
getMessage()
// 获取异常信息,返回字符串。
toString()
// 获取异常类名和异常信息,返回字符串。
printStackTrace()
// 获取异常类名和异常信息,以及异常出现在程序中的位置,并打印到控制台
举例:
try{
System.out.println(10/0);
}
catch(ArithmeticException | NullPointerException e){
System.out.println(e.getMessage());
System.out.println(e.toString());
e.printStackTrace();
}
- 多分支
try{
// 可能出现异常的代码
}
catch(异常类型 对象名){
// 对异常的处理操作
}
catch(异常类型 对象名){
// 对异常的处理操作
}
catch(异常类型 对象名){
// 对异常的处理操作
}.....
注意:
如果说,在多catch分支的情况下,如果不同的catch分支,处理的异常类型,有父子关系。那么就一定要注意,处理子类的异常分支写在前面,父类的异常分支写在后面
抛出异常,上层处理
throws关键字
概念:
在方法定义时使用,声明该方法可能抛出的异常,对于编译时异常,可以在语法层面强制方法调用者处理该异常。
语法:修饰符 返回值 方法名(形参列表) throws 异常列表 {}
说明:
- 异常列表:
异常类型1, 异常类型2, ....
,用逗号隔开,列表中的异常不要出现父子关系,如果有,那么编译器只会强制处理父类 throws + 运行时异常
:没有意义,因为运行时异常会自动抛出,不需要声明throws + 编译时异常
:有意义,这实际上是编译异常处理的一种方式,在方法中声明throws + 编译时异常
,声明可能抛出编译时异常,该方法被调用时就要处理这个编译异常- 处理编译时异常
- 方法内部
try-catch
- throws向上抛,如果在main中就别抛了,处理一下
- 方法内部
子类重写父类方法注意:
- 子类方法不能比父类抛出更多的编译时异常
- 父类如果抛出Exception,那么子类就可以随便抛出
- 建议子类重写的时候保持跟父类一样的异常列表
throw关键字
概念:
- 在方法体中使用
- 主动在程序中抛出异常
- 每次只能抛出确定的某个异常对象
针对运行时异常:
throw + 运行时异常对象(new 创建出来的)
eg:
throw new ArithmeticException(”出现了算数异常“);
针对编译时异常:
需要结合throws关键字一起使用,不要这里处理,交给方法的调用者处理(这里直接enter + alt
)
throw + 编译时异常对象(new 创建出来的)
eg:
private static void func() throws CloneNotSupportedException{
throw new CloneNotSupportedException(”出现了clone异常“);
}
throws VS throw
throws
- 用在方法声明后面,跟的是异常类名
- 可以跟多个异常类名,用逗号隔开
- 表示抛出异常,由该方法的调用者来处理
- throws表示出现异常的一种可能性,并不一定会发生这些异常
throw
- 用在方法体内,跟的是异常对象名
- 只能抛出一个异常对象
- 表示抛出异常,可以由方法体内的语句处理
- throw则是抛出了异常,执行throw则一定抛出了某种异常
finally
概念:
被finally控制的语句体一定会执行,用于释放资源,在IO流操作和数据库操作中会见到
语法:
方式一:
try{
}catch(){
}catch(){
}.....
finally{
// 一定执行
}
方式二:
try{
}finally{
}
注:
- try代码块如果有return
- 程序会先执行完finally代码块,回过头执行try中的return
- catch代码块中如果有return,并且catch正常捕获异常执行
- 程序会先执行完finally代码块后,再回去执行catch中return,从catch代码块中结束方法
- finally代码中有return
- 不会影响finally代码块执行
- 如果finally和catch中都有return
- 程序会直接从finally代码块中的return结束方法
- 如果try中的异常不能正常捕获,但是finally中有return
- 注意此时程序会跳过这个异常,不会抛出异常给JVM报错
final VS finally VS finalize
- final关键字,最终的,最后的。可以修饰类 成员变量 成员方法
- 修饰类,该类不能被继承
- 修饰变量表示一个常量
- 修饰方法表示无法重写的方法
- finally代码块,和try…catch一起使用,具有必然执行的特点
- 异常处理体系当中,用于资源释放
- finalize方法,作用是作用是在垃圾回收器回收对象之前,允许对象执行一些清理操作。
异常策略选择
- 运行时异常,应当在代码的测试阶段修改代码
- 编译时异常,功能内部能够处理的就处理,如果不能够或者没有必要处理,就
throws
关键字向上抛出
自定义异常
概念:现有的异常体系不满足需求
自定义编译时异常
- 定义一个类继承
Exception
- 构造方法
eg:
// 定义编译时异常
class MyException1 extends Exception{
public MyException1() {
}
public MyException1(String message) {
super(message);
}
}
自定义运行时异常
- 定义一个类继承
RuntimeException
- 构造方法
eg:
// 定义运行时异常
class MyException2 extends RuntimeException{
public MyException2() {
}
public MyException2(String message) {
super(message);
}
}