【转】JAVA异常处理
如何处理代码的异常,怎样的异常处理机制是最有效的,而且最能帮助我们发现和查找问题。本文对JAVA的异常机制进行了介绍,同时介绍了如何使用异常的指导原则。
异常机制是指当程序出现错误后,程序如何处理。具体来说,异常机制提供了程序退出的安全通道。当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器。 传统的处理异常的办法是,函数返回一个特殊的结果来表示出现异常(通常这个特殊结果是大家约定俗称的),调用该函数的程序负责检查并分析函数返回的结果。这样做有如下的弊端:例如函数返回-1代表出现异常,但是如果函数确实要返回-1这个正确的值时就会出现混淆;可读性降低,将程序代码与处理异常的代码混叠在一起;由调用函数的程序来分析错误,这就要求客户程序员对库函数有很深的了解。
JAVA的异常处理机制提供了很好的解决方案。通过抛出JDK预定义或者自定义的异常,能够表明程序中出现了什么样的异常情况;而且JAVA的语言机制保证了异常一定会得到恰当的处理;合理的使用异常处理机制,会让程序代码清晰易懂。
下图是java异常类结构图

Java提供了3种可抛出结构(throwable)
第一类是Error,Error表示程序在运行期间出现了十分严重、不可恢复的错误。
第二类是RuntimeException, RuntimeException 是一种unchecked Exception,即表示编译器不会检查程序是否对RuntimeException作了处理,在程序中不必捕获RuntimException类型的异常,也不必在方法体声明抛出RuntimeException类。RuntimeException发生的时候,表示程序中出现了编程错误,所以应该找出错误修改程序,而不是去捕获RuntimeException。
第三类是一般的checked Exception,继承自Exception,程序必须做异常处理,编译器会对此作检查,要么在方法体中声明抛出checked Exception,要么使用catch语句捕获不然不能通过编译。
《Effective java》提出使用异常的原则:
1. 只针对异常的情况才使用异常
1)异常应该只用于异常的情况,不应该用于正常的控制流
2)设计良好的API不应该强迫它的客户端为了正常的控制流而使用异常
2. 对可恢复的情况使用检测性异常,对编程错误使用运行时异常
1)如果期望调用者能够适当地恢复,对于这种情况就应该使用检查性异常
如何处理代码的异常,怎样的异常处理机制是最有效的,而且最能帮助我们发现和查找问题。本文对JAVA的异常机制进行了介绍,同时介绍了如何使用异常的指导原则。
40
2)用运行时异常表明编程错误。如API调用者没有遵循API规范建立的约定。
3. 避免不必要地使用检测性异常
4. 优先使用标准的异常(JDK定义的异常)
1)使API更加易于学习和使用
2)对于用到这些API的程序可读性更好
5. 抛出与抽象相对应的异常
1)如果方法抛出异常与它所执行的任务没有明显的联系,这会让人很疑惑。更高层的实现应该捕获底层的异常,同时抛出可以按照高层抽象进行解释的异常。
2)尽管异常转译与不加选择的从底层传递异常的做法相比有所改进,但不能滥用
6. 每个方法抛出的异常都要有文档
1)要单独声明检测性异常,并且利用Javadoc的@throws标记,准确的记录下抛出每个异常的条件。如果一个方法可能抛出多个异常,不要快捷的抛出这些异常的超类
7. 在细节消息中包含能捕获失败的信息
1)为捕获失败异常的细节信息应该包括导致产生该异常的参数和域的值
8. 努力使失败保持原子性
1)当对象抛出异常后,该对象仍然保持在一种定义良好的可用状态中。异常的方法调用应该使对象保持在被调用之前的状态。
9. 不要忽略异常
2)空的catch块会使异常达不到应有的目的。即使可用忽略也应该注释为什么。
处理异常会导致性能下降
异常到底慢在哪呢,有人做了个测试
循环1万次,建立Object对象:575817 ns
循环1万次,建立Exception对象:9589080 ns
循环1万次,建立、抛出并接住异常对象:47394475 ns
写一个抛异常的方法,看其字节码
public void catchException() { try { throw new Exception(); } catch (Exception e) { } }
然后使用javap -verbose命令输出它的字节码,结果如下:
public void catchException(); Code: Stack=2, Locals=2, Args_size=1 0: new #58; //class java/lang/Exception new指令首先会在常量池找到第58项常量,java.lang.Exception类的直接引用,并开辟相应大小的内存空间,把该空间压入栈顶 3: dup //复制1份栈顶值,重新压入栈顶 4: invokespecial #60; //Method java/lang/Exception."<init>":()V 将exception对象引用出栈,调用Exception类的实例构造器 7: athrow //检查栈顶异常对象类型;异常对象的引用出栈;搜索异常表,找到匹配的异常handler;重置pc寄存器状态清理操作栈;把异常对象引用入栈;异常方法栈帧逐个出栈;终止当前线程…在找不到匹配的异常handler就终止线程 8: astore_1 9: return Exception table: from to target type 0 8 8 Class java/lang/Exception
字节码是解释语义的,不能说明性能问题。但从这些字节码可以看出athrow这个指令做了很多操作。如果各位想了解跟多底层具体做了什么,可以查看该博客http://icyfenix.iteye.com/blog/857722
原作者:黎波

浙公网安备 33010602011771号