java 异常指的是破坏原来正常程序运行指令的事件,分三种:
- checkedException
- runtime exception
- Error
checkedException 受检测异常是一中预期的能够恢复的 ,比如:文件类型 打印机没纸了。这些我们可以事先处理这种情况并提醒客户,客户可以重新选择文件和加纸张就可以恢复了
runtime exception运行时异常,程序内部的错误的逻辑或者错误的使用API,一般是不可预期和恢复的,比如:空指针,数组越界等异常
Error 程序外包引起,比如内存,io等硬件,比如:内存异常 ,IOException
Error 可以看作是虚拟机错误 ,runtime exception运行时异样看作是应用的错误 ,checkedException 受检测异常看作是操作错误
异常使用过程中需要注意的几点
- 只针对异常的情况才使用异常
- 对可恢复的异常使用检测异常 ,对于编程错误使用运行异常
- 避免不必要地使用受检的异常
- 优先使用标准的异常
- 抛出与抽象相对应的异常
- 每个方法抛出的异常都要有文档
- 在细节消息中包含能捕获失败的信息
- 努力使失败保持原子性
只针对异常的情况才使用异常,如:
//糟糕的代码示例
try{
int i = 0;
while(true)
range[i++].climb();
}catch(){}
//标准模式
for(Mountain m:range)
m.climb();
有开发者可能认为遍历会进行检测,为了提供效率故而采用第一种代码方式,有三种错误
1.不符合异常机制设置的初衷
2.把代码放在try-catch块中 可能会阻止现代jvm本来可能会执行的优化
3.遍历不会执行多余的检测,已经执行优化
对可恢复的异常使用检测异常 ,对于编程错误使用运行异常
如果期望调用的能够适当的恢复,对于这种情况使用检测异样
避免不必要地使用受检的异常
异常虽然能增加代码的可靠性,但也会增加不方便,如果方法抛出一个或多个受检测异常,那么调用方法就需要多个catch来处理这些,也算是一中负担,如果一个方法只抛出单一的异常,可以考虑避免使用受检异常。比如用if 检测一下条件
优先使用标准的异常,api自带一些常用的异常,我们可以复用这些异常,减少类的加载。
抛出与抽象相对应的异常,如果方法返回与处理业务无关的异常,这种情况将会使人不知所措,如果方法直接传递了低层的异常,不仅使人困惑,而且污染了高层API,为了避免这个问题,需要进行异常转译
//异常转译
try{
...//低层异常
}catch(LowerException e){
throw new HigherException();
}
每个方法抛出的异常都要有文档, 是指方法抛出的异常需要注明什么情况会出现这种异常
在细节消息中包含能捕获失败的信息,包含能够所有“对异常有贡献的”参数和域的值,方便后续解决问题,这里的细节消息需要和用户消息区分,细节消息是为了分析失败原因
努力使失败保持原子性,产生的任何异样都应该让对象保持在该方法调用之前的状态,比如下面代码 ,如果去掉检测size ,试图从一个空栈弹出元素,这时还是会抛出异常,然而,这将导致size域保持不一致负数的状态。导致后面操作失败 ,比如增加等。
public Object pop(){
if(size==0){
throw new EmptyStackException()
}
Object result = elements[--size]
elements[size]=null
return result
}