`
aihhd2008
  • 浏览: 24616 次
  • 性别: Icon_minigender_1
  • 来自: 合肥
最近访客 更多访客>>
社区版块
存档分类
最新评论

java异常处理

    博客分类:
  • java
 
阅读更多
只针对不正常的条件才使用异常

异常只应该被用于不正常的条件,它们永远不应被用于正常的控制流。

下面是一个用异常作遍历结束条件的滥用异常的例子:
//horrible abuse of exceptions. Don't ever do this!
try{
  int i=0;
  while(true)a[i++].f();
}catch(ArrayIndexOutOfBoundsException e){
  ...
} 

其错有三:


1、创建、抛出和捕获异常的开销是很昂贵的。因为它的初衷是用于不正常的情形,少有jvm会它进行性能优化。

2、把代码放在try-catch中会阻止jvm实现本来可能要执行的某些特定的优化。

3、有些现代的jvm对循环进行优化,不会出现冗余的检查。


完全可以使用标准的实现方式:

for(int i=0;i<a.length;i++){
    a[i].f();
} 

  这条原则也适用于API设计。一个设计良好的API不应该强迫它的客户为了正常的控制流而使用异常。如果类中有一个”状态相关”的方法,即只有特定的条件下可被调用的方法,则这个类也应有一个单独的“状态测试”方法,以为调用这个状态相关方法前的检查。如Collection类的next方法和 hasNext方法。
for(Iterator i=collection.iterator();i.hasNext();){
    Foo foo=(Foo)i.next();
    ...
}  

对于可恢复的条件使用被检查的异常,对于程序错误使用运行时异常
    java提供了三种可抛出的异常:被检查的异常(checkedException)、运行时异常(run-time Exception)和错误(error)。  如果期望调用者在调用时出现的异常能够恢复,则应该使用被检查的异常,通过抛出一个被检查的异常,强迫调用者在catch中处理该异常,或者将异常传播到外面。
  对于一个方法声明要抛出的每一个被检查的异常,它是对API用户的一种潜在指示:与异常相关联的条件是调用这次个方法的一种可能结果。
  两种未被检查的可抛出结构:运行时异常和错误,在行为上相同的,它们都不需要、也不应该被捕获的抛出物。你所实现的所有未被检查的抛出结构都应是 RuntimeException的子类。定义一个非Exception、RuntimeException或Error子类的抛出物是可行的,但从行为意义上它等同于被普通的被检查异常(即Exception子类而非RuntimeException子类).

避免不必要地使用被检查的异常
    与返回代码不同,被检查的异常强迫程序处理例外的情况,从而大大地提高了程序的可靠性。而过分地使用被检查的异常,则增加了不可忽视的负担。如果正确地使用API并不能阻止这种异常条件的产生,并且一旦产生了异常,使用API的程序可以采取有用的动作,那么这种负担被认为是正当的。
try{
   ...
}catch(TheCheckedException e){
   e.printStackTree();
   System.exit(1);
} 

  如果使用API的程序员无法做得比这更好,那么未被检查的异常可能更为合适。在实践中,catch几乎总有断言失败的特征。
  “把被检查的异常变成未被检查的异常”的一种技术是,把这个要抛出异常的方法分成两个方法,第一个方法返回一个boolean以指明是否要抛出异常,另一个执行真正的功能,如果条件不满足就抛异常。如下:
//Invocation with checked exception
try{
   obj.action(args);
}catch(TheCheckedException e){
   //Handle exception condition
} 
  转换为:
//Invocation with state-testing method and unchecked exception
if(obj.actionPermitted(args)){
   obj.action(args));
}else{
   //handle exception condition
} 

  当然这种转换并不总是合适的,例如一对象将在缺少外部同步的情况下被并发访问,或者可被外界改变状态,那么这种转换将是不合适的。
尽量使用标准的异常
Java平台库中讫今为止最常被重用的异常如下:
IllegalArgumentException 参数值不合适
IllegalStateException 对于这个方法调用而言,对象的状态不合适(如初始化不恰当)
NullPointerException 在null被禁止的情况下参数值为null
IndexOutOfBoundsException 下标越界
ConcurrentModificationException 在禁止并发修改的情况下,对象检测到并发修改
UnsupportedOperationException 对象不支持客户请求的方法
其它的异常也可以使用,只要确保抛出异常的条件与文档要求一致即可。

抛出的异常要适合于相应的抽象
  高层的实现,应该捕获低层的异常,同时抛出一个可以按照高层抽象进行解释的异常,这种做法叫做 异常转译(exception translation)。即如:
//exception translation!
try{
  //use lowlevel abstraction to do our bidding
  ...
}catch(LowerLevelException e){
  throw new HigherLevelException(...);
} 

  低层的异常被高层的异常保存起来,且高层的异常提供一个公有的访问方法来获得低层的异常,这种做叫做异常链接(exception chaining)。
//Exception chaining.
try{
  //use lower-level abstraction to do our bindding
  ...
}catch(LowerLevelException e){
  throw new HigherLevelException(e);
}
  异常链的实现非常简单,在1.4及以后版本中,可以通过Throwable来获得支持。
//Exception chaining in release 1.4 or later
HigherLevelException(Throwable t){
  super(t);
} 

处理来自低层的异常,
最好的做法是,在调用低层方法之前通过一些检查等手段来确保它们会成功执行;
其次的做法是,让高层处理这些异常,从而将高层方法的调用者与低层的问题隔离开;
一般的做法是使用异常转译;
如果低层方法的异常对高层也是合适的,则将其从低层传到高层。

每个方法抛出的异常都要有文档
      总是要单独地声明被检查的异常,并且利用javadoc的@throws标记,准确地记录下每个异常被抛出的条件。
      使用javadoc的@throws标签积累下一个方法可能会抛出的每个未被检查的异常,但是不要使用throws关键字将未被检查的异常包含在方法的声明中。
      如果一个类中的许多方法出于同样的原因而抛出同一个异常,那么在该类的文档注释中对这个异常做文档,而不是为每个方法单独写一个文档。

在细节消息中包含失败 - 捕获信息
为了捕获失败,异常信息应该尽可能多的包含有意义的参数和域的值,如:IndexOutOfBoundsException应该包括上界、下界以及实际的下标。

努力使失败保持原子性
      一个失败的方法调用应该使对象保持“它在被调用之前的状态”,方法如下:
① 使用非可变对象;
② 在可变对象上的操作,应在执行前检查参数的有效性;
③ 写一段恢复代码,在执行失败的情况下,回滚到操作开始之前的状态(不常用);
④ 在对象的一份临时拷贝上执行操作,当完成之后,再把临时拷贝中的结果复制给原来的对象,如Collections.sort在执行排序之前,先把它的输入转储到一个数组中,这样既能降低内循环的开销,又能保证在排序失败的时候,输入列表将保持原样。

不要忽略异常
      除非有特殊的需求(如动画中的多帧图像播放),否则不要吃掉异常,至少在catch块中包含一条说明,用来解释为什么忽略这个异常。简单地将一个未被检查的异常传播到外界至少会使程序迅速地失败,从而保留了有助于调试该失败条件信息,比异常被忽略后的一个不可预测的时刻程序失败这种情况要强。


分享到:
评论
6 楼 aihhd2008 2010-02-25  
cleanerje 写道
aihhd2008 写道
cleanerje 写道
aihhd2008 写道
AvengerBevis 写道
恩,《Effective Java》摘巴摘巴,就能看起来像自己写的一样。

不能摘录一下吗?。。。。。。

最好写上在哪里转载的哟。
其实处理异常最麻烦的是在一个开发框架中怎样处理异常。
比如:何时该抛出异常,何时该自己处理异常,何时该对异常进行转义处理等等。
书上写的只是基本哦。

对哦,书上也是讨论一些常见的异常捕获,一旦在项目中实施就需要如何去搭建设计一个比较好的异常捕获方法

自己设计一个简单的框架就知道了,呵呵。
目前我设计的框架是这样:
展示层,只处理业务级别的异常。用户使用流程上的问题,可以进行展示,并不处理什么数据库访问失败啊、IO错误啊之类的。用户不应该在这里看到这些计算机术语。
业务层,捕获DB层的异常,并可抛出用户业务级别的异常,不过一般来说还是用返回值比较恰当。这里常常需要将程序内部的异常翻译成用户级别异常。不过很多时候,不需要抛出异常,只需要告诉展示层:没取到数据。对于用户异常,我一般是专门保存在一个XML中的,这样维护和修改都非常方便。
DB层,只对正常的数据逻辑进行访问和更新,所有IO、SQL类的错误全部抛出。可以处理一些自己可以处理的异常。一般的书里面只是将这部分的。
UTIL层,常用的工具都在这里,一般会根据约定抛出异常或者是返回null。可参考java的包。


我刚刚才进入此行业,刚刚才上项目,以后还得多向你们请教啊。。。。
5 楼 cleanerje 2010-02-25  
aihhd2008 写道
cleanerje 写道
aihhd2008 写道
AvengerBevis 写道
恩,《Effective Java》摘巴摘巴,就能看起来像自己写的一样。

不能摘录一下吗?。。。。。。

最好写上在哪里转载的哟。
其实处理异常最麻烦的是在一个开发框架中怎样处理异常。
比如:何时该抛出异常,何时该自己处理异常,何时该对异常进行转义处理等等。
书上写的只是基本哦。

对哦,书上也是讨论一些常见的异常捕获,一旦在项目中实施就需要如何去搭建设计一个比较好的异常捕获方法

自己设计一个简单的框架就知道了,呵呵。
目前我设计的框架是这样:
展示层,只处理业务级别的异常。用户使用流程上的问题,可以进行展示,并不处理什么数据库访问失败啊、IO错误啊之类的。用户不应该在这里看到这些计算机术语。
业务层,捕获DB层的异常,并可抛出用户业务级别的异常,不过一般来说还是用返回值比较恰当。这里常常需要将程序内部的异常翻译成用户级别异常。不过很多时候,不需要抛出异常,只需要告诉展示层:没取到数据。对于用户异常,我一般是专门保存在一个XML中的,这样维护和修改都非常方便。
DB层,只对正常的数据逻辑进行访问和更新,所有IO、SQL类的错误全部抛出。可以处理一些自己可以处理的异常。一般的书里面只是将这部分的。
UTIL层,常用的工具都在这里,一般会根据约定抛出异常或者是返回null。可参考java的包。

4 楼 aihhd2008 2010-02-25  
cleanerje 写道
aihhd2008 写道
AvengerBevis 写道
恩,《Effective Java》摘巴摘巴,就能看起来像自己写的一样。

不能摘录一下吗?。。。。。。

最好写上在哪里转载的哟。
其实处理异常最麻烦的是在一个开发框架中怎样处理异常。
比如:何时该抛出异常,何时该自己处理异常,何时该对异常进行转义处理等等。
书上写的只是基本哦。

对哦,书上也是讨论一些常见的异常捕获,一旦在项目中实施就需要如何去搭建设计一个比较好的异常捕获方法
3 楼 cleanerje 2010-02-25  
aihhd2008 写道
AvengerBevis 写道
恩,《Effective Java》摘巴摘巴,就能看起来像自己写的一样。

不能摘录一下吗?。。。。。。

最好写上在哪里转载的哟。
其实处理异常最麻烦的是在一个开发框架中怎样处理异常。
比如:何时该抛出异常,何时该自己处理异常,何时该对异常进行转义处理等等。
书上写的只是基本哦。
2 楼 aihhd2008 2010-02-25  
AvengerBevis 写道
恩,《Effective Java》摘巴摘巴,就能看起来像自己写的一样。

不能摘录一下吗?。。。。。。
1 楼 AvengerBevis 2010-02-25  
恩,《Effective Java》摘巴摘巴,就能看起来像自己写的一样。

相关推荐

Global site tag (gtag.js) - Google Analytics