java_seven_net的专栏

记录我的成长历程! ~ bless seven&water better and better!~~

原创  异常 收藏

1.基本概念

如果某个方法不能以正常的方式完成它的任务,在JAVA中,该方法并不返回任何值,而是抛出一个封装了错误信息的对象,这个方法将会立刻退出,并不返回正常情况下的值,此外,调用这个方法的代码也将无法继续执行,取而代之的是,异常处理机制开始搜索能够处理这种错误条件的异常处理器(exception handler)。

 

异常分为RuntimeException和非RuntimeException(曾经能够正常运行,但由于某种情况程序不能正常运行)。

 

如果是RuntimeException,一定是编程者自身问题。应该通过检查数组下标是否越界来避免ArrayIndexOutOfBoudsException异常;应该在使用变量之前检测是否为空开杜绝NullPointerException异常。

 

派生于RuntimeException类的所有异常称为“未检查异常”,其他异常称为“已检查异常”,编译器检查是否为所有“已检查异常”提供异常处理器(如IOException)。

对“已检查异常”要么进行异常处理,要么抛出。通常应该捕获知道如何处理的异常,而将不知道如何处理的异常传递出去。

 

创建异常类:

习惯上,定义的类应该包含两个构造器,一个是默认构造器,一个是带有详细描述信息的构造器。

class FileFormatException extends IOException

{

   public FileFormatException() {}

   public FileFormatException(String gripe)

   {

      super(gripe);

   }

}

 

String readData(BufferedReader in) throws FileFormatException

{

   . . .

   while (. . .)

   {

      if (ch == -1) // EOF encountered

      {

         if (n < len)

            throw new FileFormatException();

      }      . . .

   }

   return s;

}

 

 

异常捕获:

如果异常发生,在任何地方没有捕获它,程序就会终止,并在控制台上显示异常信息,其中包括异常的类型和堆栈的内容。

可以再次抛出异常:

在一个catch子句中,也可以抛出一个异常,目的是希望改变异常的类型。可能是因为抽象层次不同,也有可能是开发一个供其他程序员使用的子系统,用于表示子系统的异常类型可能有多种解释。

 

finally子句:

主要用于释放资源。不管是否有异常发生,finally都会在方法退出之前执行,即使在try中执行return返回,也会在返回之前执行finally

 

2.使用异常原则

 

个人原始想法:

异常处理不能代替简单的测试。我认为针对“未检查异常”,都应该使用简单测试。创建和捕获异常要花费大量的时间和资源。

异常和异常处理也属于程序处理流程分枝的一种,但和一般的程序处理流程(如if else)有所不同,例如ifelse,它是可进行显式判断的。

感觉有两种异常情况。一种,比如空指针异常,运行时异常,如果没有相应的异常处理,很有可能导致程序异常终止,但我感觉这种运行时异常是不应该让其发生的,应该使用if判断引用是否为空,再进行其他操作。此种情况还有数组越界等等。

而什么时候使用异常处理呢?这属于另一种情况,我感觉只有那种不能显式判断,只有在运行时,才能知道是否好用的情况,才使用异常处理,例如Class cl=Class.forName("aa");,一般这样的如果在写程序时没有进行异常处理,IDE会进行提示的。

我感觉一个函数“返回特定特征码”的方式,并不是处理异常,而是属于正常情况下的几种有限情况。

 

 

原则1

一个良好的API不应该强迫它的客户为了正常的控制流而使用异常。

如果一个类具有一个“状态相关”的方法,具体解决方式有两种:“状态测试方法”和“可被识别的返回值”

“状态测试方法”如list中的hasnext()next()的状态测试方法;

“可被识别的返回值”指的是“状态相关”的方法被调用时,如果对象处于不适合的状态之中,则返回一个可被识别的值,比如null

如何选择:

如果一个对象在缺少外部同步的情况下被并发访问,因为在调用“状态测试”方法和调用“状态相关”方法的时间间隔中,对象的状态可能发生改变,所以应该使用“可被识别的返回值”;

如果一个单独的“状态测试”方法必须重复“状态相关”方法的工作,那么从性能角度考虑,应该使用“可被识别的返回值”;

如果所有其他方面都是等同的,那么“状态测试”略优于“可被识别的返回值”,因为具有更好的可读性,对于使用不当的情况,更加易于检测和改正。

 

原则2

对于可恢复的条件使用被检查的异常,对于程序错误使用运行时异常。

什么是“可恢复的条件”?如果正确地使用API并不能阻止异常的发生,并且一旦产生了异常,使用API的程序员可以采取有用的动作。例如,IOException发生时资源可以释放。

自己创建的异常,在行为上等同于被检查异常,不要轻易声明自己的异常。

 

原则3:尽量使用标准的异常

 

原则4:抛出的异常要适合于相应的抽象

有异常转译和异常连接两种方式。

异常转译:高层的实现捕获低层的异常,同时抛出一个可以按照高层抽象进行解释的异常。

异常连接:把低层异常保存到高层异常内。

做法:(1)首先应该尽量避免异常的发生。在调用低层方法之前确保它们会成功执行,例如在给低层传递实参之前,显式地检查实参的有效性。

2)如果无法阻止来自低层的异常,让高层处理这些异常,从而将高层方法的调用者与低层问题隔离开。在这种情况下,可以用某种适当的记录设施(如日志java.util.logging)将低层的异常记录下来。

3)如果既不能阻止来自低层的异常,也无法将它们与高层的用户隔离开,一般的做法是使用异常转译。

 

原则5:在细节消息中包含异常信息

异常的字符串表示不应该与“针对用户层次的错误信息”混为一谈,后者对于最终用户而言必须是可理解的,而对于“异常的字符串”内容比可理解性更重要。

可以通过在异常的构造函数中以参数的形式引入有用信息,例如,对于IndexOutOfBoundsException异常,上界、下界和当前下标都是有用信息,最好把它们显示在异常字符串中,便于查找错误。

 

原则6:努力使失败保持原子性

一般而言,如果一个方法调用失败,应该使对象保持在该失败方法被调用之前的状态。

做法:(1)设计一个非可变对象,如String

2)在执行操作之前,检查参数的有效性,及时发现异常(我感觉使用“返回特定特征码”,客户无法区分,才使用异常)。

3)对计算处理过程调整顺序,使得任何可能失败的计算部分都发生在对象状态被修改之前。

4)编写一段恢复代码,由它来解释操作过程中的失败,以及使对象回滚到操作开始之前的状态(不常用),一般用于永久性的数据结构。

5)在对象的一份临时拷贝上执行操作,当操作完成以后,再把临时拷贝中的结果复制给原来的对象。如果数据被保存在临时的数据结构中,计算过程会更加快速,那么这种方法很使用。

 

发表于 @ 2007年04月03日 17:46:00 | 评论( loading... ) | 编辑| 举报| 收藏

旧一篇:4隐藏具体实现 | 新一篇:反射

  • 发表评论
  • 评论内容:
  •  
Copyright © java_seven_net
Powered by CSDN Blog