Java异常的优点与缺陷,及其处理原则

      最近在作一个读取数据库元数据的框架,其中的数据库的检查异常让人印象深入。try-catch简直让人抓狂,同时做为框架哪些异常时应该抛出来给调用人员,哪些是应该本身处理掉的,抛出来的异常时检查异常仍是非检查异常都值得深思。下面不少仅仅是我的观点,但愿你们补充和指出不对之处。html

 

Java理念:结构不佳的代码不能运行。(泛型这点作的不好)前端

优势:一、用强制规定的形式来消除错误处理过程当中为所欲为的因素;(C语言printf没人检查,scanf则会)。java

二、可以下降错误处理代码的复杂度。(不须要太多的if-else)程序员

三、携带信息,易于发现问题。(得益于强大的StackTrace栈轨迹)web

 

什么是异常?spring

在当前环境下没法获取必要的信息来解决的,而且会影响当前方法或者做用域继续执行的问题。前面半句,你无法解决的问题,就交给你上一级来处理,所谓的抛出异常。若是你能解决的,你没解决,那就是你的程序有问题。下半句,可有可无的异常,你能够处理掉(要留下信息),log4j找不到properties总不能让系统直接崩溃,就只这个理。数据库

Try块的意义编程

方法内部抛出异常,这个方法就结束了(向更高层抛出)。若是你不但愿结束,能够用try块。Try就是把可能抛出同样的方法集中处理。在没有异常处理的语言中,你可能须要不停的检查每一个操做是否正确,在Java中能够集中处理。这对应的就是异常的优势2。api

异常说明(检查异常的由来)网络

Java鼓励人们把方法可能抛出的异常告知使用此方法的客户端程序员。

Void f() throws TooBig,TooSmall,DivZero

说明方法f,有且只会抛出上面三个异常(RuntimeException除外)。Java认为这种种优雅的作法,可是也照成了很大的诟病。IO、数据库操做的时候,程序中会包含大量的异常处理。若是程序很是大,try-catch-finally简直就是灾难。

         Spring jdbc中的JdbcTemplate把全部的数据库异常转换为DataAccessException异常,转换为一个Runtime异常。它进行二次封装的理由就是不须要用户关心特定数据库的细节,不须要强制用户使用try-catch。它在抛出的异常中,会把原来的信息给保存起来,即所谓的异常链。这很是重要,异常链对于调试的人员来讲很是有用。Hibernate的SchemaExport就是一个糟糕的设计。

         JdbcTemplate还阻止了一些不可能处理异常的抛出,如关闭数据库链接的异常,就算抛出给上层。上层一般也是没法处理的,还不如catch掉,而后用日志的形式输出。

 

 

使用心得或注意方式

一、  须要释放资源的,必定要放在finally里面。

  finally块中的代码是必定会被执行的。若是一下代码,在try中就有return,可是结束“照样”会输出。因此,当程序中包含数据库链接、IO、网络链接等须要释放资源的状况时,必定要使用finnally,避免没必要要的消耗。

Int f(){

    try{

          return 0;
  }finally{
         System.out.println(“结束”);
  }
}

在hibernate4.0版本之前,它就犯过资源没有释放的问题。

 

 

  在 Configuration.buildSessionFactory() 函数中:settings变量包含持有链接池的对象。获取的链接对象。若是SessionFactoryImpl建立异常,conn也将得不到释放。

二、  finally中毫不使用return与throw

finally里中若是抛出异常或者使用return,本来应该抛出的异常会丢失。以下所示,本意在f()中应该是要抛出Runtime异常的,不过在finnally中有了return就再也不能捕捉到异常了。

static void f(){
      try{

        System.out.println("here");

        throw new RuntimeException("my exception");

      }finally{

        return;

      }

   }
   public static void main(String[] args) {

      f();

   }

 

三、  若是要调用传入的参数的某个方法,必定要Null检查。或者该信息必不可少。

  若是不进行null检查,虚拟机也会帮你自动抛出runtime的NEP错误。因此,不少人并不在乎这件事情。若是你的方法会被别人调用,且别人看不到你的方法代码,那问题就大了。

   static void f(String str){

      System.out.println(str.length());

   }
   public static void main(String[] args) {

      f(null);

}

  如上图的代码,抛出的异常信息显示,f方法中的第9行出现了NPE。可是,极可能别人调用你的代码时是已经编译过的class。在这种状况下,调用者就很难发现问题。因此,必定要进行异常检查,抛出一个新的并带有相关的提示信息。开源框架的代码都会进行这样的检查,如jdbcTemplate的某个方法,经过Assert.notNull对action进行了相关检查,若是为null抛出runtime异常。

 

四、  检查异常与非检查异常

Java的异常结构体系以下所示:

 

  其中的RuntimeException及其子类异常时非检查异常。在编程时不强制你显示处理。若是一个异常是致命的且不可恢复而且对于捕获该异常的方法根本不知如何处理时,或者捕获这类异常无任何益处,一般应该定义这类异常为非检查异常,由顶层专门的异常处理程序处理;像数据库链接错误、网络链接错误或者文件打不开等之类的异常通常均属于非检查异常。这类异常通常与外部环境相关,一旦出现,基本没法有效地处理。

  Spring Dao选择了把全部SQLException转换成了DataAccessException异常,是runtime的。这样,编程的代结构和可读性有了巨大的提高。如数据库关闭异常的,甚至直接catch并记录相关过程,连异常也不抛出。

  而对于一些具有能够回避异常或预料内能够恢复并存在相应的处理方法的异常,能够定义该类异常为检查异常。像通常由输入不合法数据引发的异常或者与业务相关的一些异常,基本上属于检查异常。当出现这类异常,通常能够通过有效处理或经过重试能够恢复正常状态。

五、  异常视角

 

使用者不该该接触Java异常,程序不该该在没有提示的状况下忽然崩溃。Java像如今应用比较普遍的地方应是web方面。Tomcat之类的web容器可以很好的处理异常,通常状况内部的逻辑错误不会形成应用的崩溃。异常信息也会在前端页面显示,在开发阶段或者内部测试期间应该直接暴露异常信息至前端,这样有利于报告错误。正式发布阶段,应该将500等错误直接跳转至专门的页面。

六、  catch异常后冲洗throw异常,应该包含异常链

异常链对于程序调试尤其关键。异常链的保留能够追溯异常源,这样有利于异常的正确和及时定位。异常链的丢失,有的时候绝对是致命的。

 

参考: http://www.ibm.com/developerworks/cn/java/j-lo-exceptionframework/index.html?ca=dat

相关文章
相关标签/搜索