`

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

分享到:
评论

相关推荐

    java 数字格式转换异常(NumberFornatException)

    java 数字格式转换异常(NumberFornatException) java 数字格式转换异常(NumberFornatException)

    java 异常案例

    javajava 异常案例,欢迎大家参考,希望对大家有用

    java 异常种类总结【转】.docx

    java 异常种类总结【转】.docx java 异常种类总结【转】.docx java 异常种类总结【转】.docx

    Java异常处理.md

    Java异常处理机制是一种用于有效管理程序运行时错误的方法。在Java中,通过try、catch和finally语句来实现异常捕获与处理: 1. **基础异常捕获**: - 当代码执行过程中出现如除数为零的`ArithmeticException`等...

    java+poi+word转pdf的简单demo,执行转换main方法不会抛异常

    java项目使用poi把word文档转成pdf文档, 网上很多文章的都会报错,其实就是依赖的问题,参考了无数文章和自己摸索的一套依赖,执行word转pdf文档绝不会抛异常,完美运行...

    Java异常与错误处理中英文翻译

    Java异常与错误处理的中英文翻译,供新手查阅

    [JAVA]二进制字符串转十进制带异常抛出

    JAVA 二进制 字符串 转 十进制 异常 抛出

    用java写的数制转换计算器

    我也是java初学者,编写了一个简单的数制转换计算器,可以完成常见数制见得任意装换,处理异常,设计人性化,希望对大家有帮助!

    学习Java,你需要知道这些——Java异常

    文章目录异常处理的概念异常的基本概念Java异常处理机制的优点错误的分类异常的分类预定义的一些常见异常异常的处理抛出异常捕获异常的语法生成异常对象声明自己的异常类 异常处理的概念 在程序在运行的时候可能会...

    Java代码转换成Objective-C的工具

    J2ObjC 是一个来自 Google 的开源命令行工具,用于将 Java 代码转成 iOS 平台上的 Objective-C 代码。该工具使得 Java 代码可作为 iOS 应用构建的一部分,而且无需对生成的文件进行编辑。其目标是为了编写非 UI 应用...

    C++转JAVA入门总结

    1. 内置数据类型 2. string类 3. 数组 4. 循环分支 5. 工具类(数据容器、日期、...7. JAVA异常 8. JAVA继承 1. 抽象类与抽象方法 2. JAVA接口 3. JAVA泛型编程 4. JAVA序列化 5.JAVA网络与多线程 6. JAVA类生命周期

    java汇率转换

    JAVA代码能实现多种货币转换,有抛出异常,图形界面

    java的汇率转换系统

    很全的java汇率转换系统 包含了对话框 异常处理 而且代码段

    matlab2012a java调用 图解说明 异常 测试代码

    本文档详细介绍了java调用matlab2012a及matlab2012a安装的完整过程,图解的说明文档只针对win8 64位。附加测试代码以及环境配置可能出现的问题。

    java源码包---java 源码 大量 实例

    Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM...

    JAVA_API1.6文档(中文)

    javax.transaction 包含解组期间通过 ORB 机制抛出的三个异常。 javax.transaction.xa 提供定义事务管理器和资源管理器之间的协定的 API,它允许事务管理器添加或删除 JTA 事务中的资源对象(由资源管理器驱动程序...

    Java开发技术大全(500个源代码).

    nestException_3.java 异常嵌套示例3 outBoundError.java 下标越界异常 throwException.java 抛出异常示例 throwsException.java 声明抛出异常 useMyException.java 使用自定义异常 第7章 示例描述:本章学习...

    java 读写word 转html 等操作实例及jar包

    java 读写word 转html 等操作实例及jar包

    java 方法的流程控制与异常处理

    方法的流程控制与异常处理 (1) 编写Java应用程序,求1!+2!+...+20!的和并显示,要求用方法实现求阶乘。 (2) 编写Java应用程序,从键盘输入一个整数,将其转换为的二进制、十六进制并把相应的表示输出到屏幕上。 (3) ...

    JAVA SE 开发手册.CHM

    1、JDK的安装和环境变里的配置 2. HelloWorld程序 3、JAVA的数据类型及...17、JAVA异常Exception 18、JAVA线程之基础介绍 19、I0流之基本简介 20、I0流之字符流、字节流、转换流、缓冲流、对象流 21,I0流之HIO

Global site tag (gtag.js) - Google Analytics