山穷水复疑无路,柳暗花明又一村。——《游山西村》 — 宋
·
陆游
异常总述
总述
异常是指程序在执行期间发生了意外。常说代码不可能永无bug(bushi),异常就发生在代码的编写和执行上。Java提供了大量的内置异常类,我们可以将之归结为语法错误、运行时错误和逻辑错误。语法错误在常用的编辑器中就能发现,通常会以红线标识,运行时错误发生时会抛出异常信息到控制台,而逻辑错误是最难纠正的,需要对代码的逻辑进行重新梳理。
异常和错误的区别
错误是程序运行时遇到硬件或操作系统不能完成相应指令的错误。而异常是运行环境正常情况下遇到的运行时异常情况。
异常总览
总的来说,Java异常分为检查异常和非检查异常,即分为编译时检查异常和运行时异常。所有异常类都是包含在java.lang包下,顶级父类是Throwable,下有error和Exception两大分支。见图如下
我们通常所说异常是Exception下的异常类,当发生异常时,最常用的方法是toString()方法和printStackTrace(),前者打印描述当前异常信息到控制台,后者打印当前异常对象的堆栈使用轨迹。
抛出和捕获异常
当程序运行时发生了异常,会被监听器监听到,于是形成了一个异常事件,异常事件会生成异常对象,将异常对象提交给JVM,它会找到相应的异常处理方法处理该异常对象,这个过程称为抛出异常。
当异常抛出后,虚拟机根据方法的调用链查找到异常处理的方法,同时把异常对象也给该方法,这个过程称为捕获异常。
常见异常
我们在平时编码过程中可能会常常遇见ClassNotFundException、IOException(受检查异常)和RuntimeException(非检查异常),RuntimeException下的子类我们可能会常遇到的有NullPointException,IndexOutOfBoundsException等,受检查异常和非检查异常都是继承自Exception,它们又有很多相应异常的实现。如下
异常处理
异常处理过程
JVM跟踪异常信息是根据方法调用栈去查找异常的,方法调用栈的各个方法组成了一个方法调用链,如下。
从main()方法开始一层一层的调用,JVM根据方法调用栈来跟踪,例如:当methodC方法正常执行完毕后就会将该方法从栈中删除;若是该方法产生了异常,看该方法室是否有try…catch…finally语句块处理该异常,若没有,就得在该方法头部加上thows关键字抛出异常,就将该异常交给上层调用方法,再看该方法能否处理异常,以此类推。若是所有方法都不能处理该异常,那么就会触发JVM调用printStackTrace()方法打印出异常堆栈使用轨迹,并终止程序运行。
异常处理手段
try…catch…finally语句块处理
语法说明:try语句块是处理可能会发生异常的代码,catch语句是捕获异常并对异常进行处理,finally也能被当做catch语句块处理,通常是用作“收尾工作”。此外要注意的是try必须与catch或finally之一或全部搭配使用,否则将会报错。当然,catch语句块可以是一个或多个,当catch语句块是零个时,必须有finally。
注意:当catch语句中有break,continue,return语句时,当执行完这些语句后,若有finally语句,程序仍会执行完finally语句,然后才退出循环、继续循环和方法返回的操作。若是在catch中执行System.exit(0)后,此时不会执行finally语句(若有)。
thows和thow处理
1 thow通常在方法中,通过判断自己不能或不便处理异常时会通过thow抛出异常对象。抛给上层调用该方法的方法。
2 thows通常可以在后面跟多个异常类,在方法的头部,一旦发生异常抛给上层调用。
自定义异常
Java确实已经提供了大量的内置异常,但仍有一些特殊情况我们需要自定义异常,例如学生成绩大于100或年靓大于100等等。
自定义异常都需要继承Exception或其子类,当自定义的异常也可继承于自己定义的其他异常。同时,我们需要定义自定义异常类的数据成员和重写相应的方法。需要注意的是,自定义异常只能有thow抛出异常对象给上层调用处理,因此在编码时需要处理好对应的异常情况。
作者言:有遗漏或错误的地方,还请朋友们多多指正,感激不尽!
参考书籍
[1] 崔淼、赵晓华 Java程序设计教程 [M] 北京:机械工业出版社 2019.5 ISBN 978-7-111-62467-7