什么是异常?
在《Java编程思想》中这样定义异常:阻止当前方法或作用域继续执行的问题,该问题会导致程序执行失败。在C语言中不存在异常机制,是以返回值形式告知调用方以及相对应的处理代码。因此C语言代码中正常流程代码与异常处理的代码混在一起的,没有严格的区分或分界。Java语言实现异常处理机制,正常流程代码与异常处理代码有了严格的区分。使得正常流程因异常不能再执行时,转到异常代码块处理异常。
Java异常处理语法
关于try...catch...finally语法这里不在讲述。
Java异常体系
Throwable类是Java语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能通过Java虚拟机或者Java throw语句抛出。类似地,只有此类或其子类之一才可以是catch子句中的参数类型。
Error是Throwable
的子类,用于指示合理的应用程序不应该试图捕获的严重问题。指不可恢复的,也不会捕获的异常。比如:内存异常。这里使用错误概念描述更合适。
Exception 类及其子类:这样异常应用程序可以捕获并且需要处理的异常。比如ClassNotFoundException, CloneNotSupportedException,IllegalArgumentException,IllegalStateException。
应用程序在执行过程中发生异常,如果应该程序可以处理该异常则处理异常,否则上抛给上级调用者,上级调用者不处理则直接交给JVM,JVM处理方式直接在输出流里打印出堆栈信息,终止程序执行。异常处理比起正常流程代码执行效率上要低得多,所以切记不要使用异常处理作为正常流程执行使用。
受检异常与运行时异常
运行时异常是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
受检异常是Exception类及其子类但不包含RuntimeException及子类异常。从程序语法角度讲是应用程序必须处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等异常。或者称为编译期异常。 该类异常一般是由外部因素产生的,并且可以恢复的,即使应用程序正确,也会由外界因素引发。如
网络连接有问题引发IOException。这里所说的恢复指外界因素排除后,应用程序可以继续恢复执行。
主要异常
使用通用的异常,便于代码阅读和易懂,所以推荐大家使用jdk提供的一些异常类。
受检类异常
ClassNotFoundException:类未找到异常。未找到指定字符串名的类异常。
CloneNotSupportedException:克隆异常。当调用 Object
类中的clone
方法复制对象,但该对象的类未实现Cloneable
接口时,抛出该异常。
IllegalAccessException:类,方法,字段访问异常。当应用程序试图反射创建一个实例(而不是数组)、设置或获取一个字段,或者调用一个方法,但当前正在执行的方法无法访问指定类、字段、方法或构造方法的定义时,抛出 IllegalAccessException。
InstantiationException:实例化异常。当应用程序试图使用 Class
类中的newInstance
方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常。
IOException,SQLException:分别是输入输出异常;sql异常(提供关于数据库访问错误或其他错误信息的异常)。这两个类还有许多子类,可以详细考察。
运行时异常
ArithmeticException:当出现异常的运算条件时,抛出此异常。
BufferOverflowException:当相关 put 操作达到目标缓冲区限制时,抛出此未经检查的异常。
ClassCastException:类转化异常。
IllegalArgumentException:向方法传递了一个不合法或不正确的参数时抛出异常。
IllegalStateException :在非法或不适当的时间调用方法时抛出异常。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。
IndexOutOfBoundsException:排序索引(例如对数组、字符串或向量的排序)超出范围时抛出异常。
NullPointerException:空指针异常。
抛出与高层相对应的异常
有两种方式
异常转译:更高层的实现应该捕获底层的异常,同时抛出可以按照高层抽象进行解释的异常。代码如下
try {
//调用底层方法
......
} catch (LowerLevelException e) {
e.printStackTrace();
throw new HigherLevelException(...);
}
异常链:使用支持链的异常构造器,好处是允许抛出适当的高层异常,同时又能捕获底层的异常进行失败分析。代码如下
try {
//调用底层方法
......
} catch (LowerLevelException cause) {
throw new HigherLevelException(cause);
}
当然如果底层方法抛出是异常与高层申明的异常一致就可以直接抛出,不需要上述两种方式。
努力使失败保持原子性
即执行中的对象恢复到异常抛出前状态。
1.使用不可变对象。
2.调整代码顺序,使得任何可能会失败的计算部分都在对象状态被修改之前发生。
3.写一段恢复代码。异常发生时执行这段代码。
4.在临时的拷贝对象上执行,当操作完成时再用临时拷贝对象中的结果代替对象的内容。操作异常时,临时对象丢弃。