java 异常(转)

1.提倡异常封装 
      Java语言的异常处理机制可以确保程序的健壮性,提供系统的可用率,但异常的API通常是比较"低级" 什么是低级别?那就是不包含业务的,只是程序员才能看懂的异常信息,而对于用户来说,基本就是看天书,什么是NullPointException?这些都是纯计算机语言的描述,而对于用户而言是需要业务级别的异常信息,所以我们提倡对异常的封装,异常封装有3个优势 

      1.提高系统的友好性,由于系统提供的异常都是低级的,例如打开一个文件,如果文件不存在,则会报出FileNotFoundException,此时若然我们不对异常进行封装直接将栈信息输出到用户端,那用户根本就不知道是什么意思,我们应该对异常进行业务性的说明,例如提示文件不存在等. 

      2.提高系统的可维护性,我们很多时候喜欢将多个异常信息直接使用一个异常的父类Exception或RuntimeException来表达,这种做法是不推荐的,就算将异常栈信息输出,维护人员还是要追寻到代码的层面才可以定位到异常发生的地方,这对系统的维护性造成了很大的影响,我们应该分开编写异常信息. 

      3.解决Java异常机制自身的缺憾,Java当中的只要代码触发异常,就会跳到这个异常对应的catch块当中处理,而忽略其他catch块,这对于单独的一个代码块是没有问题的,但若然是多个代码块或一个链式的处理,链当中可能多一个节触发了异常,但我们最终是会看到一个异常,所以使用异常封装就能实现记录多个异常信息,请观察以下代码 

Java代码   收藏代码
  1. public class MyException extends RuntimeException {  
  2.   
  3.     private static final long serialVersionUID = 701064983280219053L;  
  4.     private List<Throwable> exceptionList = new ArrayList<Throwable>();  
  5.   
  6.     public MyException(List<? extends Throwable> cause) {  
  7.         this.exceptionList.addAll(cause);  
  8.     }  
  9.   
  10.     // 调用时  
  11.     public static void main(String[] args) {  
  12.         List<Throwable> exceptionList = new ArrayList<Throwable>();  
  13.   
  14.         // 第一代码片段  
  15.         try {  
  16.   
  17.         } catch (Exception e) {  
  18.             exceptionList.add(e);  
  19.         }  
  20.   
  21.         // 第二代码片段  
  22.         try {  
  23.   
  24.         } catch (Exception e) {  
  25.             exceptionList.add(e);  
  26.         }  
  27.   
  28.         // 第三代码片段  
  29.         try {  
  30.   
  31.         } catch (Exception e) {  
  32.             exceptionList.add(e);  
  33.         }  
  34.   
  35.         // 正式抛出异常  
  36.         if (exceptionList.size() > 0) {  
  37.             throw new MyException(exceptionList);  
  38.         }  
  39.     }  
  40. }  



2.采用异常链传递异常 
      我们的JavaEE项目一般是三层结构,持久层,逻辑层,展现层,持久层是负责读取数据的,例如我们现在需要加载一个文件作为数据,但此时报出FileNotFoundException,由于该异常是检查性异常,所以必须在持久层处理或往上抛,但此时若然使用上抛的方式,逻辑层在调用时基本无法知道持久层为什么会出现这个错误,这个错误应该是持久层最清楚的,所以我们应该把异常封装好后传递到逻辑层,请观察以下代码 

Java代码   收藏代码
  1. public void test() {  
  2.         File file = new File("/test.xml");  
  3.         try {  
  4.             FileInputStream fis = new FileInputStream(file);  
  5.         } catch (FileNotFoundException e) {  
  6.             e.printStackTrace();  
  7.             throw new MyException("文件不存在", e);  
  8.         }  
  9.     }  



在持久层封装好自定义的异常类信息后抛出到逻辑层中,逻辑层到展现层亦同理 


3.检查性异常尽量转化为非检查性异常 
      把所有检查性异常(Checked Exception)都转换为非检查性异常(UncheckedException)是不现实的,因为检查性异常是正常逻辑的一种补充处理手段,在某些条件下必须抛出检查性异常以便上层为底层的错误进行处理,那为什么非要把受检异常转换为非受检异常呢? 

      1.检查性异常使接口声明脆弱我们应该面向接口编程,但由于检查性异常大多需要抛出,这就使得在声明接口的时候,需要把抛出的异常类型也一并声明,若然在开发迭代的过程中发现实现类需要抛出更多的异常,那我们就必须要修改接口,这就是类影响接口的情景,其实我们可以看出,在很多著名的框架当中,都在使用着非检查性异常,例如Hibernate,Spring等,特别是Hibernate框架,把底层的JDBC检查性异SQLException全部隐藏了起来而只使用了Hibernate定义的非检查性异常. 

      2.检查性异常使代码可读性降低当我们逻辑层调用持久层时,若然抛出了检查性异常,我们的逻辑层必须要对其进行try catch处理,或者再往展现层抛出,这样对持久层的调用者来说意味着,每次调用持久层的方法都需要增加4行代码才能成功调用,这样大大降低了代码的可读性 

      3.受检异常增加了开发工作量我们从上面所述可以知道,我们推荐对异常进行封装,上层模块才能更好的处理和显示给用户,但这也导致了底层没完没了的封装,无疑是加重了开发的工作量,但在软件当中,几乎是不存在完美的解决方案,就例如数据库设计中的容量换效率和效率换容量一样,我们只能取其平衡的地方,适当的对异常进行封装 

检查性异常有如此多的缺点,所以我们在开发当中通常会将检查性异常转换为非检查性异常,检查性异常的父类为Exception,非检查性的异常父类为RuntimeException,只要我们封装的异常类型是继承RuntimeException就可以抛出非检查性异常,检查性异常提出的是“法律下的自由”,而非检查性异常则是“约定的自由” 



4.在使用日志框架时需要注意异常吞噬 
      使用开源日志框架,例如log4j等已经在项目当中广泛的使用,请留意以下代码 

Java代码   收藏代码
  1. public class Test {  
  2.     Logger logger = LoggerFactory.getLogger(this.getClass());  
  3.   
  4.     public void test() {  
  5.         try {  
  6.   
  7.         } catch (Exception e) {  
  8.             e.printStackTrace();  
  9.             logger.warn("错误信息", e.getMessage());  
  10.             new MyException("错误信息", e);  
  11.         }  
  12.     }  
  13. }  



      以上代码是使用日志框架对检查性异常时的公认写法,有项目维护或开发经验的人知道,在项目维护阶段出现错误时,通常客户会将日志文件发送给维护人员,然后维护人员根据日志文件中的错误定位程序进行修复,而不是将在控制台的信息直接复制给维护人员,读者要分清控制台输出和日志文件的概念,printStackTrace方法是将错误的栈信息输出到控制台中,若然此时我们没有使用logger对异常信息进行记录,那我们将会把此异常信息吞噬掉,导致定位错误时将一头雾水,当然项目可能会设置将控制台的信息一并记录到日志文件中,但在开发阶段,我们应该尽量避免这种不稳定的事情发生. 


5.不要在finally中处理返回值 
      在finllay中处理返回值,是考试和面试题中经常出现的题目,但请谨记,在项目当中绝对不要在finally中处理返回值,请留意以下代码 

Java代码   收藏代码
  1. public static int doSomething(int num) throws Exception {  
  2.     try {  
  3.         if (num < 0) {  
  4.             throw new Exception("error");  
  5.         }else{  
  6.             return num;  
  7.         }  
  8.     } catch (Exception e) {  
  9.         throw e;  
  10.     } finally {  
  11.         return -1;  
  12.     }  
  13. }  
  14.   
  15. public static void main(String[] args) {  
  16.     try {  
  17.         doSomething(-1);  
  18.         doSomething(100);  
  19.     } catch (Exception e) {  
  20.         System.out.println("永远不会到达");  
  21.     }  
  22. }  



      我们都知道无论是否触发异常运行catch块,finally都会被执行,以上的答案两个结果都返回为-1,而且调用该方法不会抛出异常,而且覆盖了try中的返回值,而且告诉了JVM,我这个方法调用正常返回,没有问题,屏蔽了异常,在Eclipse等IDE中若在finally增加返回值还会提示警告信息,所以切记finally中不要出现返回值,finally应该作释放资源用 


6.不要在构造函数中抛出异常 
      我们知道类的创建会执行构造函数,其实从语法层面来说完全可以在构造函数中抛出异常,但从系统设计角度来说,尽量不要在构造函数中抛出异常,这样的行为不仅使子类扩展父类的构造函数受到限制,而且也违背了里氏替换原则,除非该类不应该被创建或不想被反射技术创建,则应该在构造函数增加异常 


7.使用Throwable获取栈信息 
      如何做到对同一个方法(同参数)的调用返回两个不同的结果?请观察以下代码 

Java代码   收藏代码
  1. public static boolean doSomething(){  
  2.     StackTraceElement[] stes = new Throwable().getStackTrace();  
  3.       
  4.     for(StackTraceElement ste : stes){  
  5.         if(ste.getMethodName().equals("test")){  
  6.             return true;  
  7.         }  
  8.     }  
  9.     throw new RuntimeException("除test方法外,我不允许其他人调用");  
  10. }  
  11.   
  12. public static void test(){  
  13.     doSomething();  
  14. }  
  15.   
  16. public static void test1(){  
  17.     //我报错了  
  18.     doSomething();  
  19. }  



      这里对同一个方法调用,产生了两种结果,为什么呢?因为在创建一个Throwable类时,JVM会记录下栈信息,然后生成Throwable对象,这样我们就可以知道类之间调用的顺序,方法名称和当前行号等等,就可以完成只要调用者不是test方法,就产生错误 


8.异常只为异常服务 
      异常原本的用意是正常逻辑的一个补充,有时甚至会被当做业务主体来使用,就例如在finally和catch中增加业务代码,但这种做法是不推荐的,导致代码产生了坏味道,所以并不推荐在异常中做过多的业务逻辑处理,除非必须,否者应该尽量避免 


9.多使用异常作为信息 
      由于我们的应用大多是三层架构,若然不使用异常进行信息的封装传递,我们就必须定义记录信息的变量在三层中互相传递,这样的做法降低了对代码的可读性,而且忽视了Java本身提供的信息传递途径,而在持久层通过异常的封装传递到展现层,最终显示给用户,通过异常类这个载体,可以很好的完成这项工作,例如Struts2都提供了标签让我们更好的输出异常中的信息,更提供了'值栈'给我们,使用<s:debug />看一下,你会看到Exception在里面 

 

转载自http://ray-yui.iteye.com/blog/1938946

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值