核心类库D4: 异常机制(重点、简单)
1.1 基本概念
- 异常就是”不正常”的含义,在Java语言中主要指程序
运行
阶段发生的错误。 - java.lang.Throwable类是所有错误(Error)和异常(Exception)的超类。
- 其中Error主要用于描述比较严重无法通过编码解决的错误,如:JVM挂了。
- 其中Exception类主要用于描述比较轻微可以编码解决的错误,如:0作为除数。
1.2 基本分类
- java.lang.Exception类是所有异常的超类,主要子类有两大类:
- RuntimeException - 运行时异常,也叫作非检测性异常。
- IOException和其它异常 - 其它异常,也叫作检测性异常。
- 所谓检测性异常就是指在编译阶段能够被编译器检测出来的异常
- 其中RuntimeException类的主要子类:
- ArithmeticException - 算术异常
if(ib != 0)
- ArrayIndexOutOfBoundsException - 数组下标越界异常
if(pos >= 0 && pos < arr.length)
- NullPointerException - 空指针异常
if(null != str1)
- ClassCastException - 类型转换异常
if(ex instanceof IOException)
- NumberFormatException - 数字格式异常
if(str2.matches("\\d+"))
- ArithmeticException - 算术异常
- 注意:
- 当程序执行过程中发生异常又没有手动处理时,该异常由Java虚拟机采用默认方式处理,而默认处理方式就是打印:异常名称、异常原因、异常发生的位置,然后终止程序
- 数组名后面length是特征,不用加小括号
- String类后面length是方法,要加小括号
1.3 异常的避免
- 在以后的开发中尽量多使用if条件判断来避免异常的发生
1.4 异常的捕获
- 语法格式
try { // 编写所有可能发生异常的语句; } catch(异常类型 变量名) { // 编写针对该类异常处理的语句; } ... finally { // 编写无论是否发生异常都应该执行的语句; }
- 注意事项
- 当需要编写多个catch分支时,切记小类型的异常处理放在大类型的上面(如catch(Exception e)放在上面下面就不用写了)
- 懒人的写法:
catch(Exception e) {e.printStackTrace}
- finally无论是否发生异常都会执行,主要用于进行善后处理,如:关闭打开的文件等。
- 处理异常和不处理异常的区别:程序是否会继续向下执行
- 执行流程
try{ a; b; // 可能发生异常的语句 c; } catch(...) { d; } finally{ e; } //若没有发生异常时的执行流程: a b c e //若发生异常时的执行流程:a b d e
- final/finally/finalize()的区别
- final关键字:final修饰的类不能被继承,final修饰的方法不能被重写可以被继承,final修饰的成员变量必须初始化且不能被修改
- finally关键字:在异常捕获过程中做善后处理
- finalize()方法:当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法
1.5 异常的抛出
-
基本概念
在某些特殊场合中发生异常无法捕获或者不便于捕获处理时,则可以将该异常转移给该方法的调用者,这个过程就叫异常的抛出。 - 语法格式
访问权限 返回值类型 方法名称(形参列表) throws 异常类型1, 异常类型2, ... {} 如: public void show() throws IOException {}
- 方法重写的原则
- 要求方法名相同、参数列表相同、返回值类型相同,jdk1.5开始可以返回子类类型
- 要求访问权限不能变小,可以相同或者变大
- 要求不能抛出更大的异常
- 注意:
子类重写的方法不可以抛出更大的异常、平级不一样的异常,但可以抛出一样的异常、更小的异常以及不抛出异常。
1.6 自定义异常
-
基本概念
虽然Java官方提供了大量的异常类,但没有提供针对年龄不合理等情况的异常,为了使用这种具备针对性的异常,就需要程序员自定义异常类。 - 实现流程
- 自定义xxxException类继承Exception类或者其子类。
- 提供两个版本的构造方法,一个是无参构造方法,另一个是字符串作为参数的构造
- 异常的产生
throw new 异常类型(); 如: throw new NullPointerException();
- 例子
1.自定义年龄异常 public class AgeException extends Exception { private static final long serialVersionUID = 1L; public AgeException() { } public AgeException(String msg) { super(msg); // 调用父类的有参构造方法 } 2.自定义异常成员方法 public void setAge(int age) throws AgeException { if(age > 0 && age < 150) { this.age = age; } else { //System.out.println("年龄不合理!!!"); // 产生异常来表达年龄不合理的抗议 //throw new NullPointerException(); throw new AgeException("年龄不合理!!!"); } }
- throw和throws的区别
- throws写在方法的参数列表后面,后跟类型,可以跟好多类型
- throw写在方法体里面,后跟对象,只能跟一个对象
1.7 异常的总结
1.8 使用场景
-
使用抛出异常的情景主要有:
-
异常必须由容器来处理,异常时容器做出不同处理的依据和触发;
例如:有事务处理的方法中,事务相关的逻辑必须抛出异常,而不能捕获异常,否则会导致事务不回滚。
-
本地方法不知道如何处理,只有调用方才可能知道如何处理异常;
例如:一些底层的方法,其可能出现多种异常,且调用方可能根据不同的异常做出不同的处理,只能抛出异常,而且必须是具体的异常类型,而不能是笼统的Exception类型。
-
-
使用try/catch捕获异常的情景主要有:
-
程序块中语句可能的异常不能引起其他逻辑中断;
例如:缓存逻辑不能影响正常的逻辑运行,故缓存逻辑应该放在try/catch块中。
-
必须对异常进行处理,否则会降低用户使用体验。
例如:异常到了Controller层,若不处理则会返回404或500错误页面,因此,必须使用try/catch处理各种异常。
-