《高质量java程序设计》读书笔记之----异常处理(1)

       “有所为而有所不为”。学会java和学好java并用它设计并实现高效率的程序,这两者相去甚远。

       异常处理是java中最强大而又最容易误用的机制。很多人只知道大概的异常处理的语法,却没有体会异常到底可以用来做些什么,如何才能有效的使用异常,以及什么时候又不应该使用异常。总的来说,就是“有所为”是哪些,而哪些又是“有所不为”的呢?

       面对这样一个强大的工具,我们要做的第一件事情就是对是不是要使用它做出自己的决定,而异常这个东西并不是在任何情况下都适用的。因此我们应该知道何时使用,何时不是使用异常。

       而一旦决定使用异常了以后,我们又面临一个新的问题,那就是如何去使用异常呢?

这就是我们对异常应该思考的两个大的问题,也就是“干什么”和“怎么干”。

       异常处理就是向客户表示发生的非正常情况的机制,它和断言一起构成了运行时检查的两个重要角色。主要用于运行时错误(也就是程序执行时发生的错误)的处理方面。这些错误可能是数学运算溢出,磁盘不足或者文件损坏等非正常情况。当这些错误发生时,程序应该要么去尝试恢复程序的状态,要么就直接通知用户发生了异常。

 

      规则1 用时间频度来确定异常情况。

      包含异常处理的代码,就算没有异常发生,也会付出额外的代价,应当从时间频度的角度来考虑是否使用异常。对于一个方法method(),如果它的一个流程分支经常发生,甚至每次调用都会发生,那么就不应该使用异常来进行控制。

 

       java的语义规则包含两个方面的含义:一种是java类库中内置的语义检查,如数组下标越界就会引发IndexOutOfBoundsException.另一种就是我们可以自己创建新的异常,并选择何时用throw关键字来引发异常。

       异常抛出后,JVM会对调用栈进行遍历来寻找匹配的catch语句,如果最后还找不到的话,就会调用 ThreadGroup.uncaughtException()

       异常对性能的影响有两个方面:一是异常的创建,捕获和处理都需要付出代价。另一方面,就算异常没有发生,包含异常处理的代码也会比不包含异常处理的代码需要的运行时间更多(这和我们所想的不一样,原来一直以为如果不发生异常,就根本不需要为异常处理付出代价)。

       说了这么多废话,呵呵,那到底什么时候应该使用异常处理哪?答:在异常的时候使用异常处理。(^o^,先别急着骂,听我慢慢说嘛。。。)。既然我们要使用异常处理,那最基本的一个问题就必须回答:究竟什么样的情况才能算是异常情况,你又是用什么标准来评估的哪?我认为可以使用一个简单的原则:时间频度。对于一个方法method(),如果它的一个流程分支经常发生,甚至每次调用都会发生,那么就不应该使用异常来进行控制。理由很明显,首先,从性能上考虑,如果对这么频繁发生的东西进行异常处理,那么程序性能肯定会受到损害。其次,既然是经常会发生的情况,那还能叫“异常”吗?(^o^,这不是抽自己嘴巴子嘛)。

       废话多了,也真无聊,来两个例子先:
         

例1:
        FileInputStream in;
        
try
        
{
            in 
= new FileInputStream(filename);
        }

        
catch(FileNotFoundException ex)
        
{
            System.out.println(
"找不到文件:"+filename);
            
return;
        }

        
int ch;
        
try
        
{
            
while((ch=in.read())!=-1)
            
{
                System.out.println((
char)ch);
            }

            
        }

        
catch(IOException ex)
        
{
            
        }
例2
            FileInputStream in;
        
try
        
{
            in 
= new FileInputStream(filename);
        }

        
catch(FileNotFoundException ex)
        
{
            System.out.println(
"找不到文件:"+filename);
            
return;
        }

        DataInput din 
= new DataInputStream(in);
        
try
        
{
            
        }

        
catch(EOFException ex)
        
{
            
        }

        
catch(IOException ex)
        
{
            
        }
例3:
      for(Iterator i=c.iterator();ihasNext();)
            i.remove();

      先来看例1,FileInputStream的read()返回-1表示到达文件结尾处,如果我们不这么用,而是对read()方法使用异常处理,那么在每次读全部文件时就都会到达文件结尾,那么每次都要 进行异常处理,代价太大。而另一方面,使用-1来表示文件结尾不会与文件中的字符相混淆。
      再看例2,DataInput接口的readInput(),readLong(),readFloat(),readDouble()等方法都以抛出EOFException来表示到达文件结尾了。由于可能文件中存在各种数据类型,因此不能象例1一样用-1表示到达结尾处。而另一方面,读到最后一个数据后,就自然到了文件结尾,而这是不需要额外的处理的。
   最后看例3,如果我们在这用异常,那么由于每次遍历集合的时候都会遇到集合中的元素全部遍历完的情况,那就是说每次都会抛出异常,那就显然不能用异常了。
   从这几个例子可以得两种代替异常的方法:一是用特殊的返回值 。另一种是提前检查,如例3中的Iterator.hasNext();但他们并不能完全替代异常处理,有的时候异常处理是不二之选。

posted on 2006-06-03 00:07  Phinecos(洞庭散人)  阅读(1049)  评论(0编辑  收藏  举报

导航