Java异常处理和设计【转】

Java异常处理和设计html

  在程序设计中,进行异常处理是很是关键和重要的一部分。一个程序的异常处理框架的好坏直接影响到整个项目的代码质量以及后期维护成本和难度。试想一下,若是一个项目从头至尾没有考虑过异常处理,当程序出错从哪里寻找出错的根源?可是若是一个项目异常处理设计地过多,又会严重影响到代码质量以及程序的性能。所以,如何高效简洁地设计异常处理是一门艺术,本文下面先讲述Java异常机制最基础的知识,而后给出在进行Java异常处理设计时的几个建议。java

  如有不正之处,请多多谅解和指正,不胜感激。程序员

  请尊重做者劳动成果,转载请标明转载地址:http://www.cnblogs.com/dolphin0520/p/3769804.html数据库

  如下是本文的目录大纲:数组

  一.什么是异常
  二.Java中如何处理异常
  三.深入理解try,catch,finally,throws,throw五个关键字
  四.在类继承的时候,方法覆盖时如何进行异常抛出声明
  五.异常处理和设计的几个建议网络

一.什么是异常                                                                              

  异常的英文单词是exception,字面翻译就是“意外、例外”的意思,也就是非正常状况。事实上,异常本质上是程序上的错误,包括程序逻辑错误和系统错误。好比使用空的引用、数组下标越界、内存溢出错误等,这些都是意外的状况,背离咱们程序自己的意图。错误在咱们编写程序的过程当中会常常发生,包括编译期间和运行期间的错误,在编译期间出现的错误有编译器帮助咱们一块儿修正,然而运行期间的错误便不是编译器力所能及了,而且运行期间的错误每每是难以预料的。倘若程序在运行期间出现了错误,若是置之不理,程序便会终止或直接致使系统崩溃,显然这不是咱们但愿看到的结果。所以,如何对运行期间出现的错误进行处理和补救呢?Java提供了异常机制来进行处理,经过异常机制来处理程序运行期间出现的错误。经过异常机制,咱们能够更好地提高程序的健壮性。框架

  在Java中异常被当作对象来处理,根类是java.lang.Throwable类,在Java中定义了不少异常类(如OutOfMemoryError、NullPointerException、IndexOutOfBoundsException等),这些异常类分为两大类:Error和Exception。less

  Error是没法处理的异常,好比OutOfMemoryError,通常发生这种异常,JVM会选择终止程序。所以咱们编写程序时不须要关心这类异常。ssh

  Exception,也就是咱们常常见到的一些异常状况,好比NullPointerException、IndexOutOfBoundsException,这些异常是咱们能够处理的异常。jvm

  Exception类的异常包括checked exception和unchecked exception(unchecked exception也称运行时异常RuntimeException,固然这里的运行时异常并非前面我所说的运行期间的异常,只是Java中用运行时异常这个术语来表示,Exception类的异常都是在运行期间发生的)。

  unchecked exception(非检查异常),也称运行时异常(RuntimeException),好比常见的NullPointerException、IndexOutOfBoundsException。对于运行时异常,java编译器不要求必须进行异常捕获处理或者抛出声明,由程序员自行决定。

  checked exception(检查异常),也称非运行时异常(运行时异常之外的异常就是非运行时异常),java编译器强制程序员必须进行捕获处理,好比常见的IOExeption和SQLException。对于非运行时异常若是不进行捕获或者抛出声明处理,编译都不会经过。

  在Java中,异常类的结构层次图以下图所示:

  

  在Java中,全部异常类的父类是Throwable类,Error类是error类型异常的父类,Exception类是exception类型异常的父类,RuntimeException类是全部运行时异常的父类,RuntimeException之外的而且继承Exception的类是非运行时异常。

  典型的RuntimeException包括NullPointerException、IndexOutOfBoundsException、IllegalArgumentException等。

  典型的非RuntimeException包括IOException、SQLException等。

二.Java中如何处理异常                                                                 

   在Java中若是须要处理异常,必须先对异常进行捕获,而后再对异常状况进行处理。如何对可能发生异常的代码进行异常捕获和处理呢?使用try和catch关键字便可,以下面一段代码所示:

try  {
   File file =  new  File( "d:/a.txt" );
   if (!file.exists())
     file.createNewFile();
catch  (IOException e) {
   // TODO: handle exception
}

  被try块包围的代码说明这段代码可能会发生异常,一旦发生异常,异常便会被catch捕获到,而后须要在catch块中进行异常处理。

  这是一种处理异常的方式。在Java中还提供了另外一种异常处理方式即抛出异常,顾名思义,也就是说一旦发生异常,我把这个异常抛出去,让调用者去进行处理,本身不进行具体的处理,此时须要用到throw和throws关键字。 

  下面看一个示例:

public  class  Main {
     public  static  void  main(String[] args) {
         try  {
             createFile();
         catch  (Exception e) {
             // TODO: handle exception
         }
     }
     
     public  static  void  createFile()  throws  IOException{
         File file =  new  File( "d:/a.txt" );
         if (!file.exists())
             file.createNewFile();
     }
}

  这段代码和上面一段代码的区别是,在实际的createFile方法中并无捕获异常,而是用throws关键字声明抛出异常,即告知这个方法的调用者此方法可能会抛出IOException。那么在main方法中调用createFile方法的时候,采用try...catch块进行了异常捕获处理。

  固然还能够采用throw关键字手动来抛出异常对象。下面看一个例子:

public  class  Main {
     public  static  void  main(String[] args) {
         try  {
             int [] data =  new  int []{ 1 , 2 , 3 };
             System.out.println(getDataByIndex(- 1 ,data));
         catch  (Exception e) {
             System.out.println(e.getMessage());
         }
         
     }
     
     public  static  int  getDataByIndex( int  index, int [] data) {
         if (index< 0 ||index>=data.length)
             throw  new  ArrayIndexOutOfBoundsException( "数组下标越界" );
         return  data[index];
     }
}

  而后在catch块中进行捕获。

  也就说在Java中进行异常处理的话,对于可能会发生异常的代码,能够选择三种方法来进行异常处理:

  1)对代码块用try..catch进行异常捕获处理;

  2)在 该代码的方法体外用throws进行抛出声明,告知此方法的调用者这段代码可能会出现这些异常,你须要谨慎处理。此时有两种状况:

    若是声明抛出的异常是非运行时异常,此方法的调用者必须显示地用try..catch块进行捕获或者继续向上层抛出异常。

    若是声明抛出的异常是运行时异常,此方法的调用者能够选择地进行异常捕获处理。

  3)在代码块用throw手动抛出一个异常对象,此时也有两种状况,跟2)中的相似:

    若是抛出的异常对象是非运行时异常,此方法的调用者必须显示地用try..catch块进行捕获或者继续向上层抛出异常。

    若是抛出的异常对象是运行时异常,此方法的调用者能够选择地进行异常捕获处理。

  (若是最终将异常抛给main方法,则至关于交给jvm自动处理,此时jvm会简单地打印异常信息)

三.深入理解try,catch,finally,throws,throw五个关键字                   

   下面咱们来看一下异常机制中五个关键字的用法以及须要注意的地方。

1.try,catch,finally

  try关键字用来包围可能会出现异常的逻辑代码,它单独没法使用,必须配合catch或者finally使用。Java编译器容许的组合使用形式只有如下三种形式:

  try...catch...;       try....finally......;    try....catch...finally...

  固然catch块能够有多个,注意try块只能有一个,finally块是可选的(可是最多只能有一个finally块)。

  三个块执行的顺序为try—>catch—>finally。

  固然若是没有发生异常,则catch块不会执行。可是finally块不管在什么状况下都是会执行的(这点要很是注意,所以部分状况下,都会将释放资源的操做放在finally块中进行)。

  在有多个catch块的时候,是按照catch块的前后顺序进行匹配的,一旦异常类型被一个catch块匹配,则不会与后面的catch块进行匹配。

  在使用try..catch..finally块的时候,注意千万不要在finally块中使用return,由于finally中的return会覆盖已有的返回值。下面看一个例子:

import  java.io.FileInputStream;
import  java.io.FileNotFoundException;
import  java.io.IOException;
 
 
public  class  Main {
     public  static  void  main(String[] args) {
         String str =  new  Main().openFile();
         System.out.println(str);
         
     }
     
     public  String openFile() {
         try  {
             FileInputStream inputStream =  new  FileInputStream( "d:/a.txt" );
             int  ch = inputStream.read();
             System.out.println( "aaa" );
             return  "step1" ;
         catch  (FileNotFoundException e) {
             System.out.println( "file not found" );
             return  "step2" ;
         } catch  (IOException e) {
             System.out.println( "io exception" );
             return  "step3" ;
         } finally {
             System.out.println( "finally block" );
             //return "finally";
         }
     }
}

  这段程序的输出结果为:

  能够看出,在try块中发生FileNotFoundException以后,就跳到第一个catch块,打印"file not found"信息,并将"step2"赋值给返回值,而后执行finally块,最后将返回值返回。

  从这个例子说明,不管try块或者catch块中是否包含return语句,都会执行finally块。

  若是将这个程序稍微修改一下,将finally块中的return语句注释去掉,运行结果是:

  

  最后打印出的是"finally",返回值被从新覆盖了。

  所以若是方法有返回值,切忌不要再finally中使用return,这样会使得程序结构变得混乱。

 2.throws和thow关键字

  1)throws出如今方法的声明中,表示该方法可能会抛出的异常,而后交给上层调用它的方法程序处理,容许throws后面跟着多个异常类型;

  2)通常会用于程序出现某种逻辑时程序员主动抛出某种特定类型的异常。throw只会出如今方法体中,当方法在执行过程当中遇到异常状况时,将异常信息封装为异常对象,而后throw出去。throw关键字的一个很是重要的做用就是 异常类型的转换(会在后面阐述道)。

  throws表示出现异常的一种可能性,并不必定会发生这些异常;throw则是抛出了异常,执行throw则必定抛出了某种异常对象。二者都是消极处理异常的方式(这里的消极并非说这种方式很差),只是抛出或者可能抛出异常,可是不会由方法去处理异常,真正的处理异常由此方法的上层调用处理。

四.在类继承的时候,方法覆盖时如何进行异常抛出声明                        

   本小节讨论子类重写父类方法的时候,如何肯定异常抛出声明的类型。下面是三点原则:

  1)父类的方法没有声明异常,子类在重写该方法的时候不能声明异常;

  2)若是父类的方法声明一个异常exception1,则子类在重写该方法的时候声明的异常不能是exception1的父类;

  3)若是父类的方法声明的异常类型只有非运行时异常(运行时异常),则子类在重写该方法的时候声明的异常也只能有非运行时异常(运行时异常),不能含有运行时异常(非运行时异常)。

  

五.异常处理和设计的几个建议                                                         

   如下是根据前人总结的一些异常处理的建议:

1.只在必要使用异常的地方才使用异常,不要用异常去控制程序的流程

  谨慎地使用异常,异常捕获的代价很是高昂,异常使用过多会严重影响程序的性能。若是在程序中可以用if语句和Boolean变量来进行逻辑判断,那么尽可能减小异常的使用,从而避免没必要要的异常捕获和处理。好比下面这段经典的程序:

public  void  useExceptionsForFlowControl() {  
   try  {  
   while  ( true ) {  
     increaseCount();  
     }  
   catch  (MaximumCountReachedException ex) {  
   }  
   //Continue execution  
}  
    
public  void  increaseCount()  throws  MaximumCountReachedException {  
   if  (count >=  5000 )  
     throw  new  MaximumCountReachedException();  
}

  上边的useExceptionsForFlowControl()用一个无限循环来增长count直到抛出异常,这种作法并无说让代码不易读,而是使得程序执行效率下降。

2.切忌使用空catch块

  在捕获了异常以后什么都不作,至关于忽略了这个异常。千万不要使用空的catch块,空的catch块意味着你在程序中隐藏了错误和异常,而且极可能致使程序出现不可控的执行结果。若是你很是确定捕获到的异常不会以任何方式对程序形成影响,最好用Log日志将该异常进行记录,以便往后方便更新和维护。

3.检查异常和非检查异常的选择

  一旦你决定抛出异常,你就要决定抛出什么异常。这里面的主要问题就是抛出检查异常仍是非检查异常。

  检查异常致使了太多的try…catch代码,可能有不少检查异常对开发人员来讲是没法合理地进行处理的,好比SQLException,而开发人员却不得不去进行try…catch,这样就会致使常常出现这样一种状况:逻辑代码只有不多的几行,而进行异常捕获和处理的代码却有不少行。这样不只致使逻辑代码阅读起来晦涩难懂,并且下降了程序的性能。

  我我的建议尽可能避免检查异常的使用,若是确实该异常状况的出现很广泛,须要提醒调用者注意处理的话,就使用检查异常;不然使用非检查异常。

  所以,在通常状况下,我以为尽可能将检查异常转变为非检查异常交给上层处理。

4.注意catch块的顺序

  不要把上层类的异常放在最前面的catch块。好比下面这段代码:

try  {
         FileInputStream inputStream =  new  FileInputStream( "d:/a.txt" );
         int  ch = inputStream.read();
         System.out.println( "aaa" );
         return  "step1" ;
     catch  (IOException e) {
        System.out.println( "io exception" );        
          return  "step2" ;
     } catch  (FileNotFoundException e) {
         System.out.println( "file not found" );          
         return  "step3" ;
     } finally {
         System.out.println( "finally block" );
         //return "finally";
     }

  

  第二个catch的FileNotFoundException将永远不会被捕获到,由于FileNotFoundException是IOException的子类。

5.不要将提供给用户看的信息放在异常信息里

  好比下面这段代码:

public  class  Main {
     public  static  void  main(String[] args) {
         try  {
             String user =  null ;
             String pwd =  null ;
             login(user,pwd);
         catch  (Exception e) {
             System.out.println(e.getMessage());
         }
         
     }
     
     public  static  void  login(String user,String pwd) {
         if (user== null ||pwd== null )
             throw  new  NullPointerException( "用户名或者密码为空" );
         //...
     }
}

  展现给用户错误提示信息最好不要跟程序混淆一块儿,比较好的方式是将全部错误提示信息放在一个配置文件中统一管理。

6.避免屡次在日志信息中记录同一个异常

  只在异常最开始发生的地方进行日志信息记录。不少状况下异常都是层层向上跑出的,若是在每次向上抛出的时候,都Log到日志系统中,则会致使无从查找异常发生的根源。

7. 异常处理尽可能放在高层进行

  尽可能将异常统一抛给上层调用者,由上层调用者统一之时如何进行处理。若是在每一个出现异常的地方都直接进行处理,会致使程序异常处理流程混乱,不利于后期维护和异常错误排查。由上层统一进行处理会使得整个程序的流程清晰易懂。

8. 在finally中释放资源

  若是有使用文件读取、网络操做以及数据库操做等,记得在finally中释放资源。这样不只会使得程序占用更少的资源,也会避免没必要要的因为资源未释放而发生的异常状况。                                    

参考资料:http://blessht.iteye.com/blog/908286

     http://www.oschina.net/translate/10-exception-handling-best-practices-in-java-programming

       http://blog.csdn.net/snow_fox_yaya/article/details/1823205

       http://www.iteye.com/topic/72170

       http://www.blogjava.net/gdws/archive/2010/04/25/319342.html

       http://www.2cto.com/kf/201403/284166.html

       http://www.iteye.com/topic/857443

     http://developer.51cto.com/art/200808/85625.htm

     http://www.cnblogs.com/JavaVillage/articles/384483.html

     http://tech.e800.com.cn/articles/2009/79/1247105040929_1.html

     http://blog.csdn.net/zhouyong80/article/details/1907799

     http://blog.csdn.net/luoweifu/article/details/10721543                                  

     《Effective Java中文版》   

做者: 海子
         
本博客中未标明转载的文章归做者 海子和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文链接,不然保留追究法律责任的权利。
相关文章
相关标签/搜索