六种异常处理的陋习

来源:互联网 发布:粒子特效制作软件 编辑:程序博客网 时间:2024/05/22 05:27

在下面这段代码中,你能够迅速找出异常处理的六个问题吗?

1 OutputStreamWriter out = ...

2 java.sql.Connection conn = ...

3 try { // ⑸

4  Statement stat = conn.createStatement();

5  ResultSet rs = stat.executeQuery(

6   "select uid, name from user");

7  while (rs.next())

8  {

9   out.println("ID:" + rs.getString("uid") // ⑹

10    ",姓名:" + rs.getString("name"));

11  }

12  conn.close(); // ⑶

13  out.close();

14 }

15 catch(Exception ex) // ⑵

16 {

17  ex.printStackTrace(); //⑴,⑷

18 }

反例之一:丢弃异常 代码:15行-18行。 

    这段代码捕获了异常却不作任何处理,可以算得上Java编程中的杀手。调用一下printStackTrace算不上“处理异常”,只对调试程序有帮助。那么,应该怎样改正呢?主要有四个选择:

  1、处理异常。针对该异常采取一些行动,例如修正问题、提醒某个人或进行其他一些处理,要根据 具体的情形确定应该采取的动作。

  2、重新抛出异常。处理异常的代码在分析异常之后,认为自己不能处理它,重新抛出异常也不失为 一种选择。

  3、把该异常转换成另一种异常。大多数情况下,这是指把一个低级的异常转换成应用级的异常。

  4、不要捕获异常。

  结论一:既然捕获了异常,就要对它进行适当的处理。不要捕获异常之后又把它丢弃,不予理睬。   反例之二:不指定具体的异常 代码:15行。

  catch语句表示我们预期会出现某种异常,而且希望能够处理该异常。异常类的作用就是告诉Java编译器我们想要处理的是哪一种异常。再来看看前面的代码例子。我们真正想要捕获的异常是SQLException,这是JDBC操作中常见的异常。另一个可能的异常是IOException,因为它要操作OutputStreamWriter。显然,在同一个catch块中处理这两种截然不同的异常是不合适的。如果用两个catch块分别捕获SQLException和IOException就要好多了。这就是说,catch语句应当尽量指定具体的异常类型,而不应该指定涵盖范围太广的Exception类。

  另一方面,除了这两个特定的异常,还有其他许多异常也可能出现。例如,如果由于某种原因, executeQuery返回了null,该怎么办?答案是让它们继续抛出,即不必捕获也不必处理。实际上,我们不 能也不应该去捕获可能出现的所有异常,程序的其他地方还有捕获异常的机会??直至最后由JVM处理。

  结论二:在catch语句中尽可能指定具体的异常类型,必要时使用多个catch。不要试图处理所有可能 出现的异常。

  反例之三:占用资源不释放 代码:3行-14行。

   异常改变了程序正常的执行流程。这个道理虽然简单,却常常被人们忽视。如果程序用到了文件、 Socket、JDBC连接之类的资源,即使遇到了异常,也要正确释放占用的资源。为此,Java提供了一个简化 这类操作的关键词finally。

  结论三:保证所有资源都被正确释放。充分运用finally关键词。

 反例之四:不说明异常的详细信息 代码:3行-18行。

   仔细观察这段代码:如果循环内部出现了异常,会发生什么事情?我们可以得到足够的信息判断循环 内部出错的原因吗?不能。

  printStackTrace的堆栈跟踪功能显示出程序运行到当前类的执行流程,但只提供了一些最基本的信 息,未能说明实际导致错误的原因,同时也不易解读。

  因此,在出现异常时,最好能够提供一些文字信息,例如当前正在执行的类、方法和其他状态信息, 包括以一种更适合阅读的方式整理和组织printStackTrace提供的信息。

   结论四:在异常处理模块中提供适量的错误原因信息,组织错误信息使其易于理解和阅读。

   反例之五:过于庞大的try块 代码:3行-14行。

   经常可以看到有人把大量的代码放入单个try块,实际上这不是好习惯。这种现象之所以常见,原因 就在于有些人图省事,不愿花时间分析一大块代码中哪几行代码会抛出异常、异常的具体类型是什么。一些新手常常把大量的代码放入单个try块,然后再在catch语句中声明Exception,而不是分离各个可能出现异常的段落并分别捕获其异常。这种做法为分析程序抛出异常的原因带来了困难,因为一大段代 码中有太多的地方可能抛出Exception。

  结论五:尽量减小try块的体积。

   反例之六:输出数据不完整 代码:7行-11行。

   不完整的数据是Java程序的隐形杀手。较为理想的处置办法是向输出设备写一些信息,声明数据的不完整性;另一种可能有效的办法是,先缓冲要输出的数据,准备好全部数据之后再一次性输出。

  结论六:全面考虑可能出现的异常以及这些异常对执行流程的影响。

   改写后的代码

 OutputStreamWriter out = ...

java.sql.Connection conn = ...

try {

 Statement stat = conn.createStatement();

  ResultSet rs = stat.executeQuery("select uid, name from user");

  while (rs.next()){

  out.println("ID:" + rs.getString("uid") + ",姓名: " + rs.getString("name"));

 }

 }catch(SQLException sqlex){

 out.println("警告:数据不完整");

  throw new ApplicationException("读取数据时出现SQL错误", sqlex);

} catch(IOException ioex){

  throw new ApplicationException("写入数据时出现IO错误", ioex);

 }finally{

 if (conn != null) {

   try {

    conn.close();

  }catch(SQLException sqlex2){

    System.err(this.getClass().getName() + ".mymethod - 不能关闭数据库连接: " + sqlex2.toString());

  }

 }

 if (out != null) {

  try {

   out.close();   

} catch(IOException ioex2){

    System.err(this.getClass().getName() + ".mymethod - 不能关闭输出文件" + ioex2.toString());   }

 }

}


来自:http://blog.csdn.net/yakihappy/article/details/4167572