Java 异常处理

异常是指阻止当前方法或者作用域继续执行的情况。让不能执行或者不是预期执行的情况尽早被发现。Java异常体系的使用能够降低错误代码发现和处理的复杂程度。

Java异常体系

Java所有的异常都继承自Throwable,具体分为两类,error和exception。Error是程序无法通过自身恢复的情况。如OutOMemoryError,jvm崩溃等。Exception可以处理通过程序抓取后适当处理恢复正常状态,而不至于程序停止。但RuntimeException虽然能够抓取但是就客户端而言,可以做的事情非常有限,只能直接丢弃。所以Exception可以分为checked  Exception和 unchecked Exceptions。层次图如下所示。

Exception处理思想:

处理的核心思想是将该Exception转换成checked还是unchecked。Unchecked的情况该异常客户端没必要知道或者即使抛给它,它也做不了任何事情,如SQLException,它应该在服务端处理,而不应该抛到客户端去,即使抛给客户端,客户端通常也无法处理。而checked的Exception则需要客户端来处理,如FileNotFoundException客户端需要知道是文件没有找到的问题,客户端可以通过其它方法来解决这个问题如更换其它路径等。

处理异常时应该记住一句话:“客户端是否能够对该异常采取什么措施?”如果可以采取措施则应该转换成checked,抛给客户端。否则将其转化成unchecked的就地处理,如打印错误日志。

Exception处理最佳实践

    • 对于抓住的异常通常情况下不应该忽略
      catch (NoSuchMethodException e) {
         return null;
      }

      虽然抓取了异常但是却没有做任何处理,除非你确信这个异常可以忽略,不然不应该这样做。这样会导致外面无法知晓该方法发生了错误,无法确定定位错误原因。

    • 使用更具体的异常类型
      public void foo() throws Exception { }
      
      try {
         someMethod();
      } catch (Exception e) {
         LOGGER.error("method has failed", e);
      }

      上面的两个代码段都是这种情况,无论是抛出异常还是抓取异常都不应该直接使用Exception。这会导致两种情况:Exception无法提供具体的异常信息,不利于处理;强制客户端处理(抓取)一些不需要关注的异常。客户端调用第一段代码是需要知道的是具体异常,这样才能有针对性的处理,另外有些异常客户端根本不需要关注。对于第二段代码,Exception太过于宽泛,处理时候无法知道到底是什么错误,因此也就无从有针对性的处理,同时有可能抓取到的异常根本不要处理,但是因为是Exception,无法过滤。

    • 不能只提取错误信息忽略错误其它信息如堆栈
      catch (NoSuchMethodException e) {
         throw new MyServiceException("Some information: " + e.getMessage()); }

      因为只提取了异常的信息,会让异常分析变得困难,因为不能够定位异常最终位置。

    • 不提倡即对异常打日志又将其抛出
      catch (NoSuchMethodException e) {
         LOGGER.error("Some information", e);
         throw e;
      }

      这种方式会让多处打印同一个错误信息,这种日志冗余没有任何好处。只会让错误分析变得更麻烦。

    • 只catch能够处理的异常
      catch (NoSuchMethodException e) {
         throw e;
      }

      这种处理是没有意义的,对于不能够(不需要)处理的异常没必要去抓取。

    • 对于不打算处理的异常,可以直接使用finally
      try {
        someMethod();  
      } finally {
        cleanUp();  
      }

      当你确定someMethod方法抛出的异常可以忽略的时候可以这样做。但是需要注意的是,如果该异常不应该忽略,同时cleanup方法也会抛出异常,这种处理方式会直接丢弃someMethod抛出的异常,那么客户端永远不知道someMethod抛出了异常。

    • 方法抛出的异常应该是和它相关的:如一个读取文件的方法应该抛出的异常是文件读取相关的异常,而不应该是IllegalArgumentException这样无关或者Exception这样的顶层异常。

总得来说应该记住“Throw early catch late”(早点抛出异常延迟抓取异常)原则,这个原则就是说,应该在异常出现时就将其抛出,抓取应该在能够获取足够信息的时候。简单来说,底层的方法应该更多的抛出异常,异常应该更多的在顶层代码中抓取处理。

对于抛出的异常应该加文档说明,@throws。方法上应该明确说明抛出异常的情况,及抛出的异常。

同时要注意异常滥用,异常使用是有代价的,有很多情况我们可以通过返回值来确定方法执行是否顺序,而不是抛出异常。比如连接第三方的webservice失败,对于客户端来说异常毫无意义,因此使用一个返回值来确定是否连接成功更好。

 

参考:

http://www.cnblogs.com/chenssy/p/3438130.html

http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html?page=2

https://www.javacodegeeks.com/2013/07/java-exception-handling-tutorial-with-examples-and-best-practices.html

posted @ 2016-06-14 18:00  zziawan  阅读(571)  评论(0编辑  收藏  举报