`
fdqzq
  • 浏览: 8484 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

浅谈项目中异常处理

阅读更多
  一个好的项目好的异常处理机制是不可或缺的,而作为程序员对异常的处理方法的优劣对项目的调试和性能往往也会产生意想不到的效果

“本文是Exception处理的一篇不错的文章,从Java Exception的概念介绍起,依次讲解了Exception的类型(Checked/Unchecked),Exception处理的最佳实现:
  
  1. 选择Checked还是Unchecked的几个经典依据
  
  2. Exception的封装问题
  
  3. 如无必要不要创建自己得Exception
  
  4. 不要用Exception来作流程控制
  
  5. 不要轻易的忽略捕获的Exception
  
  6. 不要简单地捕获顶层的Exception”
  
  ——选自JAVADigest.Net对原文的介绍
  
  
  
  
   “JAVADigest.Net这个站点不知道大家是否经常上,就像它的名字一样,它让我们更加有效的消化Java,或者它就像个中转站一样,至少对我是这样的,有些好的可以说是非常经典的技术文章,我都是通过它第一次获得,更多的时候我是为了偷懒才上JAVADigest.Net,因为如果是近期比较经典的文章,它上边都有介绍文字和原文连接。”
  
   ——小插曲并非常荣幸地推荐JAVADigest.Net给你
  
  
  
  
  
  
  
  
  
  
   关于异常处理的一个问题就是要对何时(when)和如何(how)使用它们做到了然于心。在本文中我将介绍一些关于异常处理的最佳实践,同时我也会涉及到最近争论十分激烈的checked Exception的使用问题。
  
  作为开发员,我们都希望能写出解决问题并且是高质量的代码。不幸的是,一些副作用(side effects)伴随着异常在我们的代码中慢慢滋生。无庸置疑,没有人喜欢副作用(side effects),所以我们很快就用我们自己的方式来避免它,我曾经看到一些聪明的程序员用下面的方式来处理异常:
  
  
  
  
  public void consumeAndForgetAllExceptions(){
   try {
   ...some code that throws exceptions
   } catch (Exception ex){
   ex.printStacktrace();
   }
  }
  
  
  
  
  
  上边的代码有什么问题么?
  
  
  
  
  在回答以前让我们想想怎样才是正确的?是的,一旦程序碰到异常,它就该挂起程序而“做”点什么。那么上边的代码是这样子的么?看吧,它隐瞒了什么?它把所有的“苦水”往肚里咽(在控制台打印出异常信息),然后一切继续,从表面上看就像什么都没有发生过一样……,很显然,上边代码达到的效果并不是我们所期望的。
  
  
  
  
  后来又怎样?
  
  
  
  
  public void someMethod() throws Exception{
  }
  
  
  
  
  
  上边的代码又有什么问题?
  
  
  
  
  很明显,上边的方法体是空的,它不实现任何的功能(没有一句代码),试问一个空方法体能抛出什么异常?当然Java并不阻止你这么干。最近,我也遇到类似的情景,方法声明会抛出异常,但是代码中并没有任何“机会”来“展示”异常。当我问开发员为什么要这样做的时候,他回答我说“我知道,它确实有点那个,但我以前就是这么干的并且它确实能为我工作。”
  
  
  
  
  在C++社区曾经花了数年实践来实践如何使用异常,关于此类的争论在java社区才刚刚开始。我曾经看到许多Java程序员针对使用异常的问题进行争论。如果对于异常处理不当的话,异常可以大大减慢应用程序的执行速度,因为它将消耗内存和CPU来创建、抛出并捕获异常。如果过分的依赖异常处理,代码对易读和易使用这两方面产生影响,以至于会让我们写出上边两处“糟糕”代码。
  
  
  
  
  异常原理
  
  
  
  
  
  大体上说,有三种不同的“情景”会导致异常的抛出:
  
  l 编程错误导致异常(Exception due Programming errors): 这种情景下,异常往往处于编程错误(如:NullPointerException 或者 IllegalArgumentException),这时异常一旦抛出,客户端将变得无能为力。
  
  l 客户端代码错误导致异常(Exception due client code errors): 说白点就是客户端试图调用API不允许的操作。
  
  l 资源失败导致异常(Exception due to resource failures): 如内存不足或网络连接失败导致出现异常等。这些异常的出现客户端可以采取相应的措施来恢复应用程序的继续运行。
  
  
  
  
  Java中异常的类型
  
  
  Java 中定义了两类异常:
  
  l Checked exception: 这类异常都是Exception的子类
  
  l Unchecked exception: 这类异常都是RuntimeException的子类,虽然RuntimeException同样也是Exception的子类,但是它们是特殊的,它们不能通过client code来试图解决,所以称为Unchecked exception
  
  举个例子,下图为NullPointerException的继承关系:
  
  
  
  Figure 1. Sample exception hierarchy
  
  
  
  
  
  图中,NullPointerException继承自RuntimeException,所以它是Unchecked exception.
  
  
  
  
  以往我都是应用checked exception多于Unchecked exception,最近,在java社区激起了一场关于checked exception和使用它们的价值的争论。这场争论起源于JAVA是第一个拥有Checked exception的主流OO语言这样一个事实,而C++和C#都是根本没有Checked exception,它们所有的异常都是unchecked。
  
  
  
  
  一个checked exception强迫它的客户端可以抛出并捕获它,一旦客户端不能有效地处理这些被抛出的异常就会给程序的执行带来不期望的负担。
  
  
  
  
  Checked exception还可能带来封装泄漏,看下面的代码:
  
  
  
  
  public List getAllAccounts() throws
   FileNotFoundException, SQLException{
   ...
  }
  
  
  
  
  
  上边的方法抛出两个异常。客户端必须显示的对这两种异常进行捕获和处理即使是在完全不知道这种异常到底是因为文件还是数据库操作引起的情况下。因此,此时的异常处理将导致一种方法和调用之间不合适的耦合。
  
  
  
  
  接下来我会给出几种设计异常的最佳实践 (Best Practises for Designing the API)
  
1. 当要决定是采用checked exception还是Unchecked exception的时候,你要问自己一个问题,“如果这种异常一旦抛出,客户端会做怎样的补救?”
  
  [原文:When deciding on checked exceptions vs. unchecked exceptions, ask yourself, "What action can the client code take when the exception occurs?"]
  
  如果客户端可以通过其他的方法恢复异常,那么这种异常就是checked exception;如果客户端对出现的这种异常无能为力,那么这种异常就是Unchecked exception;从使用上讲,当异常出现的时候要做一些试图恢复它的动作而不要仅仅的打印它的信息,总来的来说,看下表:
  
  Client's reaction when exception happens
   Exception type
  
  Client code cannot do anything
   Make it an unchecked exception
  
  Client code will take some useful recovery action based on information in exception
   Make it a checked exception
  
  
  此外,尽量使用unchecked exception来处理编程错误:因为unchecked exception不用使客户端代码显示的处理它们,它们自己会在出现的地方挂起程序并打印出异常信息。Java API中提供了丰富的unchecked excetpion,譬如:NullPointerException , IllegalArgumentException 和 IllegalStateException等,因此我一般使用这些标准的异常类而不愿亲自创建新的异常类,这样使我的代码易于理解并避免的过多的消耗内存。
  
  
  
  
  2. 保护封装性(Preserve encapsulation)
  
  
  不要让你要抛出的checked exception升级到较高的层次。例如,不要让SQLException延伸到业务层。业务层并不需要(不关心?)SQLException。你有两种方法来解决这种问题:
  
  l 转变SQLException为另外一个checked exception,如果客户端并不需要恢复这种异常的话;
  
  l 转变SQLException为一个unchecked exception,如果客户端对这种异常无能为力的话;
  
  多数情况下,客户端代码都是对SQLException无能为力的,因此你要毫不犹豫的把它转变为一个unchecked exception,看看下边的代码:
  
  
  
  
  public void dataAccessCode(){
   try{
   ..some code that throws SQLException
   }catch(SQLException ex){
   ex.printStacktrace();
   }
  }
  
  
  上边的catch块紧紧打印异常信息而没有任何的直接操作,这是情有可原的,因为对于SQLException你还奢望客户端做些什么呢?(但是显然这种就象什么事情都没发生一样的做法是不可取的)那么有没有另外一种更加可行的方法呢?
  
  
  
  
  public void dataAccessCode(){
   try{
   ..some code that throws SQLException
   }catch(SQLException ex){
   throw new RuntimeException(ex);
   }
  }
  
  
  
  
  
  上边的做法是把SQLException转换为RuntimeException,一旦SQLException被抛出,那么程序将抛出RuntimeException,此时程序被挂起并返回客户端异常信息。
  
  
  
  
  如果你有足够的信心恢复它当SQLException被抛出的时候,那么你也可以把它转换为一个有意义的checked exception, 但是我发现在大多时候抛出RuntimeException已经足够用了。
  
  
  
  
  3. 不要创建没有意义的异常(Try not to create new custom exceptions if they do not have useful information for client code.)
  
  
  看看下面的代码有什么问题?
  
  public class DuplicateUsernameException
   extends Exception {}
  
  
  它除了有一个“意义明确”的名字以外没有任何有用的信息了。不要忘记Exception跟其他的Java类一样,客户端可以调用其中的方法来得到更多的信息。
  
  我们可以为其添加一些必要的方法,如下:
  
  public class DuplicateUsernameException
   extends Exception {
   public DuplicateUsernameException
   (String username){....}
   public String requestedUsername(){...}
   public String[] availableNames(){...}
  }
  
  
  
  
  在新的代码中有两个有用的方法:reqeuestedUsername(),客户但可以通过它得到请求的名称;availableNames(),客户端可以通过它得到一组有用的usernames。这样客户端在得到其返回的信息来明确自己的操作失败的原因。但是如果你不想添加更多的信息,那么你可以抛出一个标准的Exception:
  
  throw new Exception("Username already taken");
  
  
  
  
  更甚的情况,如果你认为客户端并不想用过多的操作而仅仅想看到异常信息,你可以抛出一个unchecked exception:
  
  throw new RuntimeException("Username already taken");
  
  
  
  
  另外,你可以提供一个方法来验证该username是否被占用。
  
  
  
  
  很有必要再重申一下,checked exception应该让客户端从中得到丰富的信息。要想让你的代码更加易读,请倾向于用unchecked excetpion来处理程序中的错误(Prefer unchecked exceptions for all programmatic errors)。
  
  
  
  
  4. Document exceptions.
  
  
  你可以通过Javadoc’s @throws 标签来说明(document)你的API中要抛出checked exception或者unchecked exception。然而,我更倾向于使用来单元测试来说明(document)异常。不管你采用哪中方式,你要让客户端代码知道你的API中所要抛出的异常。这里有一个用单元测试来测试IndexOutOfBoundsException的例子:
  
  public void testIndexOutOfBoundsException() {
   ArrayList blankList = new ArrayList();
   try {
   blankList.get(10);
   fail("Should raise an IndexOutOfBoundsException");
   } catch (IndexOutOfBoundsException success) {}
  }
  
  
  
  
  上边的代码在请求blankList.get(10)的时候会抛出IndexOutOfBoundsException,如果没有被抛出,将fail("Should raise an IndexOutOfBoundsException")显示说明该测试失败。通过书写测试异常的单元测试,你不但可以看到异常是怎样的工作的,而且你可以让你的代码变得越来越健壮。
  
  
  
  
  
  
  
  下面作者将介绍界中使用异常的最佳实践(Best Practices for Using Exceptions)
  
  1. 总是要做一些清理工作(Always clean up after yourself)
  
  如果你使用一些资源例如数据库连接或者网络连接,请记住要做一些清理工作(如关闭数据库连接或者网络连接),如果你的API抛出Unchecked exception,那么你要用try-finally来做必要的清理工作:
  
  public void dataAccessCode(){
   Connection conn = null;
   try{
   conn = getConnection();
   ..some code that throws SQLException
   }catch(SQLException ex){
   ex.printStacktrace();
   } finally{
   DBUtil.closeConnection(conn);
   }
  }
  
  class DBUtil{
   public static void closeConnection
   (Connection conn){
   try{
   conn.close();
   } catch(SQLException ex){
   logger.error("Cannot close connection");
   throw new RuntimeException(ex);
   }
   }
  }
  
  
  
  
  DBUtil是一个工具类来关闭Connection.有必要的说的使用的finally的重要性是不管程序是否碰到异常,它都会被执行。在上边的例子中,finally中关闭连接,如果在关闭连接的时候出现错误就抛出RuntimeException.
  
  
  
  
  2. 不要使用异常来控制流程(Never use exceptions for flow control)
  
  
  下边代码中,MaximumCountReachedException被用于控制流程:
  
  public void useExceptionsForFlowControl() {
   try {
   while (true) {
   increaseCount();
   }
   } catch (MaximumCountReachedException ex) {
   }
   //Continue execution
  }
  
  public void increaseCount()
   throws MaximumCountReachedException {
   if (count >= 5000)
   throw new MaximumCountReachedException();
  }
  
  
  
  
  上边的useExceptionsForFlowControl()用一个无限循环来增加count直到抛出异常,这种做法并没有说让代码不易读,但是它是程序执行效率降低。
  
  
  记住,只在要会抛出异常的地方进行异常处理。
  
  
  
  
  
  3. 不要忽略异常
  
  
  当有异常被抛出的时候,如果你不想恢复它,那么你要毫不犹豫的将其转换为unchecked exception,而不是用一个空的catch块或者什么也不做来忽略它,以至于从表面来看象是什么也没有发生一样。
  
  
  
  
  
  4. 不要捕获顶层的Exception
  
  
  unchecked exception都是RuntimeException的子类,RuntimeException又继承Exception,因此,如果单纯的捕获Exception,那么你同样也捕获了RuntimeException,如下代码:
  
  
  try{
  ..
  }catch(Exception ex){
  }
  
  
  一旦你写出了上边的代码(注意catch块是空的),它将忽略所有的异常,包括unchecked exception.
分享到:
评论

相关推荐

    浅谈Java中Mysql数据库的连接与操作.pdf

    在预处理阶段,常用的操作包括处理缺失值、去除噪声和异常值、数据转换和标准化等。此外,属性的选定和编辑也是预处理的重要步骤,它涉及确定哪些属性对于数据挖掘任务是重要的,哪些可以被省略。 总结来说,本文...

    论文研究-基于Java异常处理机制的研究 .pdf

    这表明异常处理不仅在理论上具有重要性,而且在实际项目开发中也是提高系统可靠性和健壮性的重要手段。 8. 关键技术点 异常处理机制的核心在于将错误和异常分离,提高程序的可读性和可维护性。通过面向对象的方法...

    浅谈SpringBoot 中关于自定义异常处理的套路

    浅谈SpringBoot 中关于自定义异常处理的套路 本文主要介绍了在 Spring Boot 项目中如何自定义异常处理的套路,包括静态异常页面和动态异常页面的定义方式。静态异常页面可以通过 HTTP 响应码来命名页面,例如 404....

    浅谈自动化控制在污水处理厂项目中的管理.rar

    这篇文章将深入探讨自动化控制在污水处理厂项目中的应用与管理,旨在揭示其对于提高效率、降低成本、保障环境质量的关键作用。 首先,我们需要理解自动化控制的基本概念。自动化控制是指利用计算机系统、传感器和...

    浅谈项目教学法在JAVA语言教学中的应用.zip

    在这些项目中,他们将接触到类的设计、对象的创建、数据结构的应用、异常处理、多线程编程等Java核心概念。通过编写代码,学生能够直观地看到每个概念如何在实际场景中发挥作用。 其次,项目教学法促进团队协作。在...

    浅谈SSH学习要点

    此外,Struts的验证框架、分发Action、Template模式和Proxy模式以及国际化和异常处理都是提升开发效率和应用质量的关键。 接下来是Hibernate,它是一个对象关系映射(ORM)框架。基础使用包括理解ORM的概念,如何...

    浅谈VB程序的调试和错误处理.

    《浅谈VB程序的调试和错误处理》这篇文章深入探讨了Visual Basic(以下简称VB)程序设计中常见的错误类型及其处理方法,对于提升VB程序的开发效率和质量具有重要意义。作者陈玉泉和陈汉松,分别来自常州工学院计算机...

    浅谈如何优雅处理JavaScript异步错误

    在JavaScript中,处理异步错误是编程实践中的一大挑战,因为它们往往不会像同步错误那样立即中断代码执行。本文将探讨两种优雅处理JavaScript异步错误的方法:try/catch 和 Golang-style(then)处理。 1. **try/...

    javaweb项目开发个人常见问题与经验浅谈.docx

    ### JavaWeb项目开发个人常见问题与经验浅谈 #### 一、业务逻辑 在JavaWeb项目的开发过程中,业务逻辑的设计尤为重要。它不仅关系到系统的稳定性,还直接影响用户体验和项目的后期维护成本。 - **对基础资料的...

    浅谈基槽检验及地基的局部处理.doc

    【标题】:浅谈基槽检验及地基的局部处理 【内容概要】:本文主要探讨了基槽检验的目的、内容以及注意事项,并详细阐述了地基局部处理的方法,对建筑施工过程中的基槽开挖后的关键步骤进行了深入讨论。基槽检验是确保...

    浅谈为什么java命令运行class文件出现异常共7页.p

    - **异常处理**:在程序中未捕获到的异常会抛出,需要检查代码中的try-catch块是否完整。 - **内存溢出**:如果程序占用过多内存,JVM可能会抛出`OutOfMemoryError`。可以调整JVM启动参数,如`-Xms`和`-Xmx`来控制...

    浅谈java!

    它包含了Java的核心概念和技术,如类、对象、异常处理、线程等。在这一阶段,学习者应重点掌握以下几点: - **语法基础**:熟悉Java的基本语法结构,如变量定义、运算符、控制流语句等。 - **面向对象**:深刻理解...

    浅谈装船机伸缩臂架处理.pdf

    标题中的“装船机伸缩臂架处理”主要涉及的是港口装卸设备的技术细节,而标签中提到了“ARM处理器”、“内核”以及“参考文献”和“专业指导”,这暗示了讨论可能涉及到使用ARM架构处理器在控制装船机伸缩臂架过程中...

    浅谈基于浅谈基于MSComm控件实现VB串口通信

    ### 基于MSComm控件实现VB串口通信的关键知识点 #### 一、MSComm控件简介 MSComm控件是Visual Basic (VB) 开发环境中的一个重要组件,主要用于...在实际开发中,还需要结合具体的项目需求进行更深入的学习和实践。

    浅谈android线程模型

    ### 浅谈Android线程模型:深入理解与实践 #### 引言 随着智能手机的普及和技术的不断进步,Google的Android操作系统成为了移动设备领域的重要力量。Android不仅为用户提供了丰富的功能,也为开发者提供了广阔的...

    浅谈智能管理系统在高校实验室管理中的应用.pdf

    浅谈电气工程及其自动化技术在智能建筑中的应用[J]. 建材与装饰, 2017(15):178-179. [2] 陈亦洋. 浅谈智能建筑中电气工程及其自动化技术[J]. 科学技术创新, 2019(07):53-54. [3] 吴震宇. 浅谈电气自动化系统在建筑...

    digester(浅谈第一次使用)

    在“浅谈第一次使用”的描述中,可能涵盖了博主初次尝试使用Digester时的经验和遇到的问题,以及逐步理解和掌握该工具的过程。由于描述信息为空,我们无法获取具体的细节,但可以假设内容可能包括了基本概念介绍、...

    浅谈隧道监控测量方法与数据处理.pdf

    隧道监控测量方法与数据处理是指在隧道施工过程中,对隧道的稳定性及其周边环境进行连续、系统的监测与测量,并对收集的数据进行分析处理。本文以贵州省隧道工程施工为例,详细探讨了隧道施工监控测量的方法,并对...

    浅谈危化企业关停涉及的税务及会计处理.docx

    然而,企业在关停过程中涉及到的税务及会计处理异常复杂,本文将对此进行详细解析。 首先,对于被关停企业员工的安置问题,根据《中华人民共和国劳动合同法》规定,企业应当向被裁撤的员工支付经济补偿。具体计算...

    浅谈软件测试计划的制定

    《浅谈软件测试计划的制定》 在软件开发过程中,软件测试是确保产品质量的关键环节,而测试计划的制定则是测试工作的重要前提。测试计划详细规定了测试的目标、方法、资源和时间表,对于保证测试的有效性和效率至关...

Global site tag (gtag.js) - Google Analytics