1. 选择Checked还是Unchecked的几个经典依据
2. Exception的封装问题
3. 如无必要不要创建自己得Exception
4. 不要用Exception来作流程控制
5. 不要轻易的忽略捕获的Exception
6. 不要简单地捕获顶层的Exception”
——选自JAVADigest.Net对原文的介绍
<o:p></o:p>
“JAVADigest.Net这个站点不知道大家是否经常上,就像它的名字一样,它让我们更加有效的消化Java,或者它就像个中转站一样,至少对我是这样的,有些好的可以说是非常经典的技术文章,我都是通过它第一次获得,更多的时候我是为了偷懒才上JAVADigest.Net,因为如果是近期比较经典的文章,它上边都有介绍文字和原文连接。”
——小插曲并非常荣幸地推荐JAVADigest.Net给你 <o:p></o:p>
<o:p></o:p>
关于异常处理的一个问题就是要对何时(when)和如何(how)使用它们做到了然于心。在本文中我将介绍一些关于异常处理的最佳实践,同时我也会涉及到最近争论十分激烈的checked Exception的使用问题。
作为开发员,我们都希望能写出解决问题并且是高质量的代码。不幸的是,一些副作用(side effects)伴随着异常在我们的代码中慢慢滋生。无庸置疑,没有人喜欢副作用(side effects),所以我们很快就用我们自己的方式来避免它,我曾经看到一些聪明的程序员用下面的方式来处理异常:
<o:p></o:p>
|
<o:p></o:p>
上边的代码有什么问题么?
<o:p></o:p>
在回答以前让我们想想怎样才是正确的?是的,一旦程序碰到异常,它就该挂起程序而“做”点什么。那么上边的代码是这样子的么?看吧,它隐瞒了什么?它把所有的“苦水”往肚里咽(在控制台打印出异常信息),然后一切继续,从表面上看就像什么都没有发生过一样……,很显然,上边代码达到的效果并不是我们所期望的。
<o:p></o:p>
后来又怎样?
<o:p></o:p>
|
<o:p></o:p>
上边的代码又有什么问题?
<o:p></o:p>
很明显,上边的方法体是空的,它不实现任何的功能(没有一句代码),试问一个空方法体能抛出什么异常?当然Java并不阻止你这么干。最近,我也遇到类似的情景,方法声明会抛出异常,但是代码中并没有任何“机会”来“展示”异常。当我问开发员为什么要这样做的时候,他回答我说“我知道,它确实有点那个,但我以前就是这么干的并且它确实能为我工作。”
<o:p></o:p>
在C++社区曾经花了数年实践来实践如何使用异常,关于此类的争论在java社区才刚刚开始。我曾经看到许多Java程序员针对使用异常的问题进行争论。如果对于异常处理不当的话,异常可以大大减慢应用程序的执行速度,因为它将消耗内存和CPU来创建、抛出并捕获异常。如果过分的依赖异常处理,代码对易读和易使用这两方面产生影响,以至于会让我们写出上边两处“糟糕”代码。
<o:p></o:p>
异常原理<o:p></o:p>
<o:p></o:p>
大体上说,有三种不同的“情景”会导致异常的抛出:
l 编程错误导致异常(Exception due Programming errors): 这种情景下,异常往往处于编程错误(如:NullPointerException 或者 IllegalArgumentException),这时异常一旦抛出,客户端将变得无能为力。
l 客户端代码错误导致异常(Exception due client code errors): 说白点就是客户端试图调用API不允许的操作。
l 资源失败导致异常(Exception due to resource failures): 如内存不足或网络连接失败导致出现异常等。这些异常的出现客户端可以采取相应的措施来恢复应用程序的继续运行。
<o:p></o:p>
Java中异常的类型<o:p></o:p>
Java 中定义了两类异常:
l Checked exception: 这类异常都是Exception的子类
l Unchecked exception: 这类异常都是RuntimeException的子类,虽然RuntimeException同样也是Exception的子类,但是它们是特殊的,它们不能通过client code来试图解决,所以称为Unchecked exception
举个例子,下图为NullPointerException的继承关系:
<v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"><v:stroke joinstyle="miter"></v:stroke><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"></v:f><v:f eqn="sum @0 1 0"></v:f><v:f eqn="sum 0 0 @1"></v:f><v:f eqn="prod @2 1 2"></v:f><v:f eqn="prod @3 21600 pixelWidth"></v:f><v:f eqn="prod @3 21600 pixelHeight"></v:f><v:f eqn="sum @0 0 1"></v:f><v:f eqn="prod @6 1 2"></v:f><v:f eqn="prod @7 21600 pixelWidth"></v:f><v:f eqn="sum @8 21600 0"></v:f><v:f eqn="prod @7 21600 pixelHeight"></v:f><v:f eqn="sum @10 21600 0"></v:f></v:formulas><v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"></v:path><o:lock v:ext="edit" aspectratio="t"></o:lock></v:shapetype>
Figure 1. Sample exception hierarchy<o:p></o:p>
<o:p></o:p>
图中,NullPointerException继承自RuntimeException,所以它是Unchecked exception.
<o:p></o:p>
以往我都是应用checked exception多于Unchecked exception,最近,在java社区激起了一场关于checked exception和使用它们的价值的争论。这场争论起源于JAVA是第一个拥有Checked exception的主流OO语言这样一个事实,而C++和C#都是根本没有Checked exception,它们所有的异常都是unchecked。
<o:p></o:p>
一个checked exception强迫它的客户端可以抛出并捕获它,一旦客户端不能有效地处理这些被抛出的异常就会给程序的执行带来不期望的负担。
<o:p></o:p>
Checked exception还可能带来封装泄漏,看下面的代码:
<o:p></o:p>
|
<o:p></o:p>
上边的方法抛出两个异常。客户端必须显示的对这两种异常进行捕获和处理即使是在完全不知道这种异常到底是因为文件还是数据库操作引起的情况下。因此,此时的异常处理将导致一种方法和调用之间不合适的耦合。
<o:p></o:p>
接下来我会给出几种设计异常的最佳实践 (Best Practises for Designing the API)
<o:p></o:p>