快精灵印艺坊 您身边的文印专家
广州名片 深圳名片 会员卡 贵宾卡 印刷 设计教程
产品展示 在线订购 会员中心 产品模板 设计指南 在线编辑
 首页 名片设计   CorelDRAW   Illustrator   AuotoCAD   Painter   其他软件   Photoshop   Fireworks   Flash  

 » 彩色名片
 » PVC卡
 » 彩色磁性卡
 » 彩页/画册
 » 个性印务
 » 彩色不干胶
 » 明信片
   » 明信片
   » 彩色书签
   » 门挂
 » 其他产品与服务
   » 创业锦囊
   » 办公用品
     » 信封、信纸
     » 便签纸、斜面纸砖
     » 无碳复印纸
   » 海报
   » 大篇幅印刷
     » KT板
     » 海报
     » 横幅

Java中Exception的处理

简述












用Java在开发系统的时候,Exception的处理往往是比较复杂的。如何处理开发中碰到的Exception,如何将合理的非常信息呈现给客户是开发人员必须要考虑的问题。












关于Exception的处理的文章在很多地方都可以看到,本文除了做一个总结之外,还将结合Design by Contract,JDK 1.4引入的assertion,以及如何用Spring的AOP处理Exception做进一步的探讨。












Exception的分类












从JDK的API中我们可以看到,Java把非常分为了Error和Exception两大类,在Exception中又分为checked exception和runtime exception。从系统开发的角度上,我们可以把exception分为:












・ JVM非常。这种非常我们不应该捕获,因为它的出现意味着一些比较严峻的错误,比如OutOfMemoryError,StackOverflowError等;





・ 系统非常。大多数情况下,系统非常以RuntimeException的形式出现,比如NullPointerException, ArrayOutOfBoundsException等,这时往往意味着我们的程序里面出现了Bug;还有一种情况,例如我们没有办法通过JNDI找到某个资源,也应该属于系统非常。系统非常的主要特点是,当我们碰到这种非常的时候,我们没有合适的办法处理,或者说我们不能已一个合理的方法告诉最终用户系统出现了什么错误。很难想象用户看到一个NPE,并在界面上看到一堆stack trace是什么感觉。这种非常应该在单元测试以及集成测试的时候被检测到,在发布的时候应该尽可能不出现这样的问题;





・ 应用非常。这种非常是由我们的系统,或者第三方系统种抛出的非常,这些非常的出现对用户来说,可能是因为某个验证没有通过,某个操作的步骤出现错误等,比如插入数据库的时候出现主键重复的情况等。总之,这些非常的信息可以通过一个用户看的懂的方法显示给用户。












Design By Contract(DBC)












我们暂时把Exception的处理放在一边,先看看Design by Contract的概念。












对于任何一个软件系统来说,一个重要的目标就是可靠性,即准确性和健壮性。系统的准确性主要看这个系统是不是符合Specificatoin,健壮性主要是指当碰到Specification没有涉及的情况,即非常情况的时候能不能以一种合理的方法解决。












DBC的主要思想是一个类和它的客户程序之间有一个合同;客户程序必须保证调用这个类之前某些前提条件必须满意(Precondition),而这个类必须保证在被调用之后的某些属性和状态是准确的(Postcondidtion/Class Invariants)。假如能有一种方法能够让编译器检查这些Precondition和Postcondition是否准确,这个合同是否被满意,那么出现的错误可以被立刻捕捉。












例如,一个类需要一个setMonth( int month )的方式,我们一般的实现方式大致如下:


public void setMonth( int month ){ if( month > 0 && month < 13 ) { throw new IllegalArgumentException( “” ); } this.month = month;}

但是按照DBC的概念,应该由客户代码,而不是setMonth方式保证传进来的参数是一个准确的数值,而setMonth应该保证当方式执行之后,month的值被准确设置,同时保证该类处在一个准确的状态。所以setMonth中对于参数month的验证就不在这里出现了。从这个例子中可以看到,DBC的引入对不同类的责任有一个明确的划分,原来的代码里面setMonth的责任现在被转移到了客户代码里面。












目前,编程语言中对DBC支持较好的是Eiffel,而在JDK1.4中引入的assertion则为Java在DBC方面提供了一些支持。












Java Assertion












Java虽然不直接支持DBC,但我们可以利用JDK1.4提供的assertion功能做一些这方面的工作。下面简朴介绍assertion的用法,具体的信息请参见sun网站上的资料(http://java.sun.com/j2se/1.4.2/docs/guide/lang/assert.html)。












Assert有两种用法





・ assert BooleanExpression和





・ assert BooleanExpression : DetailMessage












系统运行的时候假如检查到BooleanExpression是false,那么就是抛出一个AssertionError。DetailMessage假如提供,会通过AssertionError的构造函数传进去。












由于assertion是在JDK1.4引入的,为了编译包含有assertion应用的Java程序,我们需要在javac中打开开关source=”1.4”,如在ant的build文件中,我们一般这么写:












<javac srcdir=”${src.dir}” destdir=”${build.dir}” source=”1.4” target=”1.4”> <classpath refid=”class.path”/></javac>

而在IDE环境中注重做相应的变化。












在缺省情况下,assertion在运行时是被禁用的,我们通过开关-ea和-da来打开和关闭assertion的应用,如:












java ?Cea SomeClass







有了assertion的帮助,我们在上面的assertion代码中做如下的改变(注重:请参见Sun的assertion文档,这里仅仅示例assertion的用法,并没有考虑assertion的best practice中的某些原则):












public void setMonth( int month ){ assert month > 0 && month < 13; this.month = month; //assert the state of this class in valid.}

在开发之前,我们和客户代码先签订合同,我们这里在调用方式之前先检查precondition是不是已经满意,假如没有满意,我们就直接抛出AssertionError,因为我们合同里面已经做了约定,输入参数的准确性应该由客户保证(第一个assertion不应该出错),而我们这里应该保证方式被调用之后的postcondition和class invariants的准确性。












引入了assertion,我们一般在开发过程中应该打开-ea开关,这样可以在单元测试的时候能够捕获到相称数量的自己的bug,从而保证系统的健壮。












Java Assertion Best Practice












Sun的assertion使用文档上对什么时候使用assertion做了指导,对其中的一点,我们存在一定的疑问,认为Sun的提法稍显武断(谁这么胆大包天,竟敢质问Sun的伟大J).












[Do not use assertions to check the parameters of a public method.]












在我们实际开发的过程中,我们认为,Sun这里提到的public method应该理解为不同模块之间的接口,而不是单纯意义上的Java类的公用方式。假设一个小组提供一个底层支持模块给其他小组使用,而这个小组内部也分了很多层次,那么这里的Public method我们认为应该指提供应其他模块/组使用的API,而内部的某些方式,虽然是公用的,仍旧可以使用assertion,因为这个合同属于是内部合同。所以我们认为这里的public method应该指存在合同关系的两个模块之间接口的public method。












另外,assertion的使用在概念会和数据验证出现一定的重叠,这个我们需要根据实际的情况决定什么时候使用assertion。












Exception的处理












回到正题,根据上面的讨论,对于三种Exception来说,JVM的Exception我们在系统实现上不予考虑,对于一部分系统非常来说,我们可以使用assertion解决掉一部分由于系统的bug引起的非常,对于其余的Exception要准确处理,而处理的标准则是一方面能够给最终用户提供有意义的出错信息,另外一方面,由于尽管我们做了认真的测试,我们仍不能保证系统在发布之后不会出现任何问题,因此需要考虑能够有合理的方式正确定位错误,为开发人员纠错提供依据。












在实际的应用中,关于Exception类的设计我们可以借鉴一下Spring中关于对Exception的设计。通过考察Spring DAO支持中Exception的设计我们可以发现以下几个特点:












1. 所有的Exception都来自一个跟节点;





2. 包装如SQLException等非常,使错误信息没有丢失;





3. 采用RuntimeException,而不是checked exception。












在学习Spring的过程中,刚开始没有完全理解采用RuntimeException的好处,但通过对其AOP支持的理解,的确觉得采用RuntimeException有一定的道理:












1. 我们不用在一个方式后面声明这个方式需要throws XXXException;





2. 在调用这个方式的时候也不同一定要try…catch段了;





3. EJB的CMT中,也是靠抛出一个RuntimeException,EJBException,来通知轻易回滚事务,Spring用AOP治理事务的方式和它有了相通的地方(虽然Spring的事务治理中没有规定必须要抛出RumtimeException)












使用RuntimeException并没有限制我们在开发过程中一定不捕捉这些非常,假如一个系统中,上层模块需要捕捉下层模块抛出的非常,然后在其中增加信息,RuntimeException并没有限制我们这么做,相反,因为开发人员必须更加了解底层模块的情况,而不是借助现在方便的开发工具如Eclipse或者JBuilder自动生成try…catch代码段,使得我们的系统可能出错的机会得到一定程度的降低。












AOP中的throws advice












这里把这个题目加进来是因为AOP可能会给我们的设计带来一些跟以往设计不同的地方,所以这里提供一个Spring的throws advice的实例来看看AOP会给我们带来一些什么样的变化。












假设我们的系统需要记录具体的出错信息日志,按照以往的想法,我们会在系统中所有抛出非常的地方加上记录日志的代码;但是引入了AOP之后,我们可以将出错和记录日志两个模块完全解耦,通过Spring配置的方式将两个模块结合在一起。












请看下面的代码:












//定义一个Service,Public interface ItestService { Void doSomething();} Public class TestServiceImpl implements ITestService { Public void doSomehting() { Throw new RuntimeException(…); }} //定义throws advicePublic class LogAdvice implements ThrowsAdvice { Public void afterThrowing( RuntimeException ex ) throws Throwable { //Log the error information }} //Spring的配置文件<bean id="throwsAdvisor" class="com.company.LggAdvice"/><bean id="testService" class="org.springframework.aop.framework.ProxyFactoryBean"><property name="target"> <bean class="com.company.TestServiceImpl"/> </property> <property name="proxyInterfaces"> <value>com.company.ITestService</value> </property> <property name="interceptorNames"> <list> <value>throwsAdvisor</value> </list> </property></bean>

在系统运行的时候,当TestService的方式抛出非常的时候,LogAdvice中的afterThrowing方式就会被调用;同时这个advisor并不吃掉这个非常,而是继承抛出去,从而不会影响我们原来的流程。从中我们看到,两个模块之间的耦合从原来的代码中转移到了配置文件中,因此我们的设计可以充分利用AOP给我们带了的优势。






返回类别: 教程
上一教程: IReport与JasperReport开发详解二
下一教程: 全面挖掘Java Excel API 使用方式

您可以阅读与"Java中Exception的处理"相关的教程:
· java 与 mysql 中文问题的处理
· Java语言中字符的处理
· Java语言中字符的处理
· 继承讨论Exception的处理
· JAVA语言中字符的处理
    微笑服务 优质保证 索取样品