`

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异常分析及解决办法

    Java异常处理是编程中至关重要的一个环节,它帮助开发者识别并修复程序运行时出现的问题。在Java中,异常是程序执行期间发生的不正常情况,通常会导致程序中断。下面将详细解释给定文件中提到的一些常见Java异常,并...

    java 异常框架CODE

    Java异常框架是Java编程语言中处理程序运行时错误和异常的核心机制。在Java中,异常是一种特殊的对象,用于表示程序运行过程中的不正常状态。Java的异常处理模型基于\"异常处理块\",包括try、catch、finally和throw...

    java转js工具

    综上所述,"java转js工具"是一个重要的开发辅助工具,它帮助开发者在Java和JavaScript之间架起桥梁,使得跨平台开发变得更加便捷。然而,使用时需要注意语言特性的差异,以及转换过程中可能出现的问题和挑战。

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

    在Java编程语言中,`NumberFormatException`是一个常见的运行时异常,它发生在尝试将字符串转换为特定数字类型(如`Integer`, `Double`, `Float`等)时,如果字符串不能被解析为对应类型的数值,就会抛出这个异常。...

    Java 异常处理的 9 个最佳实践

    Java异常处理的9个最佳实践涵盖了开发者在进行Java编程时应当遵循的一系列规则和方法,以确保代码的健壮性和可维护性。异常处理是编程中的一个重要部分,它能够帮助我们更有效地管理和响应程序运行时的错误情况。 ...

    java2python--java代码转python工具

    4. 异常处理:Java的try-catch-finally在Python中对应于try-except-finally结构,转换时需要注意异常类型的映射。 5. 数组与列表:Java的数组在Python中通常对应为列表,但Java数组是固定大小的,而Python列表可以...

    java 异常案例

    ### Java异常处理机制详解与案例分析 在Java编程语言中,异常处理是程序设计中一个非常重要的组成部分。它能够帮助开发者有效地捕捉并处理程序运行时出现的问题,避免因错误而导致程序崩溃,从而提高软件的健壮性和...

    java异常处理

    Java异常处理是编程中至关重要的一个环节,它允许程序员优雅地处理程序运行过程中可能出现的错误情况,确保程序的稳定性和健壮性。异常处理机制在Java中被设计为一种结构化的方法,使得程序在遇到不可预见的问题时,...

    Java转C++代码工具 J2C

    Java转C++代码工具J2C是一个专门用于将Java源代码转换为等效C++代码的工具。这个工具对于那些需要在不支持Java或者需要利用C++特定性能特性的环境中迁移Java项目的人来说非常有用。在深入探讨J2C之前,我们先理解...

    Java 异常处理的误区和经验总结

    以下是对Java异常处理的一些误区和经验总结。 **误区一:过度使用try-catch块** 有些开发者习惯于在每个函数的开始部分都套用try-catch块,以为这样可以捕捉所有可能出现的异常。实际上,这种做法使得代码变得混乱...

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

    在Java开发中,有时我们需要将Word文档转换为PDF格式,以满足不同的应用场景需求。...通过正确的依赖配置和代码实现,你就可以避免描述中提到的运行时异常,成功地在Java项目中实现Word到PDF的转换了。

    java URL转PDF文件

    值得注意的是,这个过程中可能会遇到编码问题、样式丢失、图片无法显示等问题,因此实际应用时可能需要更复杂的处理逻辑,比如处理CSS、JavaScript等,以及对异常的妥善处理。 总结一下,Java中将URL转换为PDF涉及...

    java行转列的例子

    "java行转列"这个主题是数据处理中的一个常见需求,尤其是在数据分析、报表生成或数据显示时。当我们从数据库中获取数据,有时会遇到单个字段的数据需要在用户界面上以列的形式展示,这时就需要进行数据的行列转换。...

    java常见异常总结

    为了更好地理解和处理这些异常,本文将详细介绍几种常见的Java异常类型及其处理方法。 #### 1. `java.lang.NullPointerException` - **异常概述**:`NullPointerException`是Java中最常见的运行时异常之一,它发生...

    JAVA异常大全

    Java异常处理是编程过程中的重要组成部分,它帮助开发者在程序执行期间识别并处理错误和意外情况。Java异常是程序运行时出现的不正常状态,它们通常由Java虚拟机(JVM)或者Java类库在遇到特定问题时抛出。异常分为...

    Java 代码转VB VC的小工具 特别版

    3. **异常处理**:Java 使用 try-catch-finally 结构处理异常,而 VB 和 VC 使用 Try...Catch...Finally 或 Select Case...Of 语句。转换工具需要理解这些差异并进行相应转换。 4. **标准库和API**:Java 标准库...

    JAVA wav转PCM Utils代码工具类

    `JAVA wav转PCM Utils代码工具类`提供了这样的功能,使得开发者能够便捷地进行音频格式的转换。以下是对这个工具类的详细说明: ### WAV格式 WAV是一种无损音频文件格式,由Microsoft开发,广泛用于Windows平台。它...

    Java2Pas Java代码转pas代码

    这个过程涉及到类、方法、变量、条件语句、循环、异常处理等众多语法元素的转换。由于Java和Pascal的语法差异,转换过程中可能会遇到一些挑战,比如Java中的匿名内部类在Pascal中可能需要不同的表示方式,或者Java的...

Global site tag (gtag.js) - Google Analytics