Best Practices for Exception Handling(异常处理最佳实践)

在JE中一为仁兄的博客中看到一篇关于异常的好文章。

原文地址:http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html

所以也想来温习哈异常知识。

优秀的程序员都想写出质量高的代码,就必须控制好异常的用法,异常用的好,可以优化代码,否则,就会产生副作用,因为如果没有把异常用好的话,异常反而会导致你的程序变慢,因为需要去花费内存和CPU资源去创建异常,抛出异常,捕获异常。还有,如果过多的使用异常,会导致程序很难看,头疼。

 

所以用好异常很重要。要不偏不倚,要小心的运用。

 

会抛出异常的三种情况:

  1.Exceptions due to programming errors。

  2.Exceptions due to client code errors

  3.Exceptions due to resource failures

 

在JAVA中,主要有两大种类型的异常:

  1.Checked exceptions:客户端代码必须处理API抛出的这类异常,该类异常继承于Exception。

  2.Unchecked exceptions所有继承于 RuntimeException的异常都特殊对待,客户端代码不需要处理API抛出的这类异常。

 

关于异常这个话题,在JAVA社区和C++社区一直辩论不休。辩论的主题起源于:JAVA好像是第一个主流的带有checked Exception的面向对象的语言。C++和C#中没有checked Exception,他们中的所有异常都是unchecked Exception。

 

到底什么是Checked Exception?

     它就是一种在低层方法中抛出,在高层调用方法中强制处理。

    如果客户端代码(client code)不能高效的处理异常,Checked Exception这种强制执行,就会加重API和client code之间的负担。

 

有的程序员会采取这样的捷径:制止异常在一个空的catch块中,或者仅仅在方法中抛出异常,其实这就是把负担转移给客户端的调用者。

 

废话说了那么多,现在来看看主题,怎么样去设计一个合适的抛出异常的API?

 1.当选择异常类型checked 或Unchecked时候,就问自己:当异常发生时,客户端代码能做什么?

      如果客户端代码能够恢复异常,就选择checked异常;如果客户端不能做什么,就选择Unchecked Exception.

输出一点错误日志,对恢复并没有作用。

 

异常类型选择标准
异常发生时客户端的反映  异常类型
客户端代码不能做任何事  unchecked  Exception
 客户端代码根据异常信息会采取有用的恢复的action  checked  Exception

 

 

 

 

 

 

 

一般对于程序性错误,更喜欢选择unchecked Exception.因为它不会强迫客户端API去明显的处理它们。一般我们更倾向地使用标准的API,不会去设计自己的Exception.

 

2.保护封装性

永远不让有特殊实现的checked Exception扩大到更高的层。比如:不要让来自数据访问层的SQLException传播到业务逻辑对象层。业务逻辑层根本不需要知道 SQLException。有两种方法可以来优化:

    一,Convert SQLException into another checked exception, if the client code is expected to recuperate from the exception.

    二,Convert SQLException into an unchecked exception, if the client code cannot do anything about it.

 

一般情况下,客户端代码不会做任何事情,所以不要迟疑去把它转换成unchecked Exception。

看看下面的代码段:

public void dataAccessCode(){
    try{
        ..some code that throws SQLException
    }catch(SQLException ex){
        ex.printStacktrace();
    }
}

 上面的catch块,仅仅禁止了代码的执行,并没有做任何事情。既然我们的客户端代码不能对异常做任何事情,为何不将上面的代码写成如下这样:

public void dataAccessCode(){
    try{
        ..some code that throws SQLException
    }catch(SQLException ex){
        throw new RuntimeException(ex);//关键,抛出unchecked Exception
    }
}

 上面的代码已经将SQLException转换成了RuntimeException,如果 SQLException发生,就将抛出一个新的 RuntimeException.这时程序的执行就会被搁置,异常被报告。这样做的好处是,不会让不必要异常处理破坏业务逻辑层的数据。

 如果你有信心在业务逻辑层能够恢复SQLException 异常,也可以将SQLException转换成一个更有意义的checked exception.但是我发现在大多数情况下, RuntimeException足以应付了。

 

3.不要创建不能给客户端代码提供任何信息的异常。

看看下面的代码有什么错误?

public class DuplicateUsernameException
    extends Exception {}

 因为它没有给客户端代码提供任何有用的信息,只是显示出是一个异常。Exception类也象其他普通的类一样,也可以在类中提供方法,给客户端代码调用而取得一些信息。

我们可以给 DuplicateUsernameException添加一些有用的方法。

public class DuplicateUsernameException
    extends Exception {
    public DuplicateUsernameException 
        (String username){....}
    public String requestedUsername(){...}
    public String[] availableNames(){...}
}

 这样客户端代码就可以调用requestedUsername()方法获取请求的用户名信息,如果不想添加额外的信息,也就可以抛出一个标准的异常,如:

throw new Exception("Username already taken");

 更好的,你如果认为客户端代码不会做任何事,只是输出一个日志信息,就可以这样做:抛出一个unchecked Exception

throw new RuntimeException("Username already taken");

 

下面是使用异常应该注意的技巧:

1. Always clean up after yourself

    如果你正在使用数据库连接或网络连接这样的资源,你必须确定清理掉它们。如果你调用的API也仅仅是调用的unchecked exception的话,使用完资源后,也要注意用try--finally blocks来清理。

比如:

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);//抛出unchecked exception
        }
    }
}

 DBUtil就是一个用来关闭连接的工具类。关键点就是finally 块的使用,无论异常是否被捕获,它都会执行。

 

 

2. Never use exceptions for flow control

 

3. Do not suppress or ignore exceptions

 

4. Do not catch top-level exceptions

 

5. Log exceptions just once

 

 

 

你可能感兴趣的:(J2SE)