Java中的异常处理机制

1       异常

异常,可以说是不可避免的,尽管小心翼翼,也时常出现。对于用java语言开发的一个应用系统来说,都应有一套自己处理异常的框架。Java本身提供了一套异常处理机制,Throwable类是java语言中所以错误和异常的超类,有两个子类Error和Exception。

 

2       异常处理

Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。

抛出异常当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。

 捕获异常在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。

能够捕捉异常的方法,需要提供相符类型的异常处理器。所捕捉的异常,可能是由于自身语句所引发并抛出的异常,也可能是由某个调用的方法或者Java运行时 系统等抛出的异常。也就是说,一个方法所能捕捉的异常,一定是Java代码在某处所抛出的异常。简单地说,异常总是先被抛出,后被捕捉的。

2.1     关键字

Java异常处理涉及到五个关键字,分别是:try、catch、finally、throw和throws

1.try、catch、finally三个语句块均不能单独使用,三者可以组成try...catch...finally、try...catch、 try...finally三种结构,catch语句可以有一个或多个,finally语句最多一个;

2. try、catch、finally三个代码块中变量的作用域为代码块内部,分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。;

3. 多个catch块时候,只会匹配其中一个异常类并执行catch块代码,而不会再执行别的catch块,并且匹配catch语句的顺序是由上到下

4.当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;

5.当try捕获到异常,catch语句块里没有处理此异常的情况:当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;

6.当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句

7.throw关键字是用于方法体内部,用来抛出一个Throwable类型的异常。

如果抛出了检查异常,则还应该在方法头部声明方法可能抛出的异常类型。该方法的调用者也必须检查处理抛出的异常。 
如果所有方法都层层上抛获取的异常,最终JVM会进行处理,处理也很简单,就是打印异常消息和堆栈信息。程序执行到throw时,立即停止。

如果抛出的是Error或RuntimeException,则该方法的调用者可选择处理该异常。有关异常的转译会在下面说明。 

8.throws关键字用于方法体外部的方法声明部分,用来声明方法可能会抛出某些异常。仅当抛出了检查异常,该方法的调用者才必须处理或者重新抛出该异常,否则程序中断。当方法的调用者无力处理该异常的时候,应该继续抛出。 

2.2     异常处理原则

1.能处理就早处理,抛出不去还不能处理的就想法消化掉或者转换为RuntimeException处理。因为对于一个应用系统来说,抛出大量异常是有问题的,应该从程序开发角度尽可能的控制异常发生的可能。 
2.对于检查异常,如果不能行之有效的处理,还不如转换为RuntimeException抛出。这样也让上层的代码有选择的余地――可处理也可不处理。 
3.对于一个应用系统来说,应该有自己的一套异常处理框架,这样当异常发生时,也能得到统一的处理风格,将优雅的异常信息反馈给用户。

2.3     异常转译与异常链

所谓的异常转译就是将一种异常转换为另一种新的异常,也许这种新的异常更能准确地表达程序发生异常。异常转译有三种情况:

1.Error到Exception,将错误转换为异常,并继续抛出。例如,WEB框架中,将org.springframework.web.servlet.DispatcherServlet的doDispatch()方法中,将捕获的错误转译为一个NestedServletException异常。这样做的目的是为了最大限度挽回因错误发生带来的负面影响。

2.Exception到RuntimeException,将检查异常转换为RuntimeException可以让程序代码变得更优雅,让开发人员集中精力设计更合理的程序代码,反过来也增加了系统发生异常的可能性。

3.Error到RuntimeException,把所有的异常和错误转译为不检查异常,这样可以让代码更为简洁,还有利于对错误和异常信息的统一处理。

 

异常链:在设计模式中有一个叫做责任链模式,该模式是将多个对象链接成一条链,客户端的请求沿着这条链传递直到被接收、处理。同样Java异常机制也提供了这样一条链:异常链。异常链是一种面向对象编程技术,指将捕获的异常包装进一个新的异常中并重新抛出的异常处理方式。原异常被保存为新异常的一个属性(比如cause)。这个想法是指一个方法应该抛出定义在相同的抽象层次上的异常,但不会丢弃更低层次的信息。

3       应用系统中的异常处理

在经典的三层架构模型中,通常都是这样来进行异常处理的:
持久层一般抛出的是RuntiomeException类型的异常,一般不处理,直接向上抛出。
业务层一般要封装自定义异常,统一向外抛出(这里要注意,如果用spring在业务层管理异常,一定要配置好异常回滚类型,因为spring默认只回滚RuntiomeException类型的)。我也见过一些想省事的人,业务层也不定义任何异常,也不进行try catch,如果业务层出现异常将在表现层进行处理及页面跳转。
表现层必须要处理业务层的异常,以正确向客户报告系统出现的问题,这里面是最后一道异常处理的地方。我也见过有懒人在业务层都不处理,直接在web.xml中配置errorPage的,但不建议这么做。

 

有关异常框架设计这方面公认比较好的就是Spring,Spring中的所有异常都可以用org.springframework.core.NestedRuntimeException来表示,并且该基类继承的是RuntimeException。Spring框架很庞大,因此设计了很多NestedRuntimeException的子类,还有异常转换的工具,这些都是非常优秀的设计思想。

 

前面说过,一般当程序发生异常时,通常异常处理可能需要做一些通用处理,如异常日志记录、异常通知,重定向到一个统一的错误页面(如 Web 应用)等。如果这些通用异常处理放置于 catch 块中,将导致大量的重复代码,从而可能引起日志冗余、同一异常的实现多样化等问题。另外,大量异常处理程序放置于 catch 块中造成程序的高耦合性。为了解决此类问题,有必要分离出异常处理程序、统一异常处理风格、降低耦合性、增强异常处理模块的复用程度。通常的异常处理模式包括业务委托模式(Business Delegate)、前端控制器模式(Front Controller)、拦截过滤器模式(Intercepting Filter)、AOP 模式、模板方法模式等。

  一般性异常处理框架

  为了解决基本异常处理结构所带来的问题,不妨把异常相关处理委托给一个专用Service 代理,从而分离出异常处理业务,以一种统一的方式和逻辑进行处理,异常 Service 主要处理两个方面:一方面是要按照实际系统要求调用通用处理程序处理异常,如日志记录、异常界面展示、异常通知等;另外一方面,需要通过过滤所接受到的异常类型,找到定制的异常处理程序进行异常处理。对于异常 Service 的应用一般可以通过在系统的顶层进行异常自动拦截(一般多层系统中尤为普遍,如放置于前端控制器 Front Controller),或者主动调用异常 Service 进行处理。

 

 

该框架主要包括三部分:异常 Service、异常处理过滤器、系统异常层次定义。

  异常 Service:整个异常框架的核心,通常用于主动拦截异常或被动调用处理异常。根据具体业务需要,调用通用服务程序进行一般化异常处理,如日志服务、异常消息通知服务等;另外,异常 Service 最主要目的用于拦截并处理异常,其需要维护定制的异常处理器链,用于特定类型异常的特定处理。

异常处理过滤器:维护系统中各种异常处理器,包括增加异常处理器、删除异常处理器、查找异常处理器操作等。其中最主要的功能是接收特定异常并找到与之匹配的异常处理器进行处理。异常处理过滤器具体实现可以通过一个配置文件维护所有异常与异常处理器的映射,另外可以通过另外一个配置文件维护系统中所有已定义的异常处理器。从而,异常处理过滤器可以通过配置文件进行初始化操作。

异常层次定义:异常层次定义应用系统的异常基础结构,是异常处理过滤器所处理的目标异常类型集合。

 

4       总结

异常需要封装和传递,我们在进行系统开发的时候,不要“吞噬”异常,也不要“赤裸裸”的抛出异常,封装后再抛出,或者通过异常链传递,可以达到系统更健壮、友好的目的。

  

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值