Java编码中异常的使用建议

作者: typeorigin | 来源:发表于2016-11-16 13:04 被阅读476次
    source_code.jpg

    异常,精确计算机世界里的不安定份子。当然说的是异常的使用,似乎没有什么统一的标准。但是我觉得在一个团队或者是系统里面还是应该遵循一定的规矩。

    无规矩不方圆。

    下面列一下目前工作中总结的一些使用经验

    首先看下Java异常的分类。

    Java 异常的分类

    • 受检异常 (Checked Exception). 异常的处理由编译器来保证,如果方法声明异常但是没处理则编译失败。
    • 非受检异常 (Unchecked Exception). 编译器不会检查的一类异常
      • RuntimeException. IndexOutOfBoundsException, IllegalArgumentException 等
      • Error. 表示很严重的问题,应用自己也搞不定, 与代码编写者无关。 OutOfMemoryError 等。 不要继承Error来定义异常

    异常机制提供了一种异常事件的冒泡处理机制,你可以选择你想处理这个异常的层次。 如果没有异常的话,你就要在很低的层次处理异常,并想办法通知上层

    异常使用中的困惑

    在我的使用或者团队大家的共识是,异常使用困惑的地方主要集中在下面两点:

    • 什么时候使用受检异常,什么时候使用非受检异常
    • 错误码和异常的使用

    所以接下来主要围绕着两个困惑来进行解答

    先来看看受检和非受检异常使用的时机,此处非受检指的是 RuntimeException

    受检异常的使用时机

    Use checked exceptions for conditions from which the caller
    can reasonably be expected to recover

    • 当可以用来表示业务流转中的异常分支,给出更合理的错误提示时
    • 当你觉得调用方可以解决异常时
    • 当异常由外部不可控的因素导致时,例如用户输入数据库异常文件不存在网络

    当你面临上面的情景时可以采用受检异常

    非受检异常的使用时机

    Use runtime exceptions to indicate programming errors (Bugs) - From Effective Java

    RuntimeException 通常指程序运行时的错误,可以引申为调用方错误的使用了API或者类库设计者的问题。

    Runtime exceptions can occur anywhere in a program, and in a typical one they can be very numerous. Having to add runtime exceptions in every method declaration would reduce a program's clarity. Thus, the compiler does not require that you catch or specify runtime exceptions - From Oracle The Java™ Tutorials

    上面说到RuntimeException是程序中非常常见的错误,因此没有强制让编译器来检查这一类错误。但是程序员自己就需要做好预先检查的工作。RuntimeException 有个好处就是能够穿透到最上层,让最上层做决定。因此可以围绕下面几点来使用RuntimeException

    • 当你处理不了底层声明的异常时,将底层异常转换为RuntimeException。
    • 当用户调用的参数不符合程序预期,使用不当时

      一个强有力的证明: IllegalArgumentException 定义的是 RuntimeException

    错误码和异常使用的时机

    在没有异常机制的程序语言中,可能比较依赖错误码等来作为业务层面的流程判断。但是在Java中提供了异常的处理机制,这种冒泡处理机制,让你可以选择你想处理这个异常的层次。 如果没有异常的话,你就要在很低的层次处理异常,并想办法通知上层。

    总结来讲,错误码和异常的特点就是

    • 异常是强类型且类型安全的分支控制技术
    • 返回错误值则是弱类型不安全

    作为Java程序员,应该优先使用异常来反馈程序中的异常情况。

    使用异常来区分正常的业务场景和错误的业务路径

        public String foo() IOException, FileNotFoundException {
            ...
        }
    
        // 一个好的使用方法应该是这样
        try {
            foo();
        } catch (IOException ioe) {
            // 给出友好的提示信息
        } catch (FileNotFoundException fe) {
            // 给出友好的提示信息        
        }
    

    一种异常和错误码的混合方案

    定义一个业务异常,然后异常中添加字段来表示错误码。

    enum ErrorCode {
        INVALID_PARAM, INVALID_PATH, INVALID_STAUS;
    }
    
    class MyException extend Exception {
        public MyException(String msg) {
            super(msg);
        }
    }
    
    // 使用场景
    public String foo() throws MyException {
        if (...) {
            throw new MyException(ErrorCode.INVALID_PARAM.getName());
        }
    
        ...
    
        if (...) {
            throw new MyException(ErroCode.INVALID_PATH.getName());
        }
    
        ...
    
        if (...) {
            throw new MyException(ErrorCode.INVALID_STATUS));
        }
    }
    
    
    

    错误码和异常使用的建议

    • 同构系统里优先使用异常来表示错误。
      • 比如两个Java类相互调用,方法之间就可以用异常来处理非正常情况,这样可以充分发挥异常类型的作用
    • 异构系统的情况使用返回错误信息的方式。
      • 比如提供一个webservice接口,就可以用不同的业务编码来表示错误情况

    最后是异常使用的一些实践

    异常处理的实践

    • 记得释放资源。特别是数据库还有网络资源,使用 try-catch-finally
    • 不要使用异常作控制流程之用
    • 不要忽略异常
    • 生产代码不要 printStatckTrace()
    • 尽量缩小异常的捕获范围

    具体来讲就是

    try-catch-finally 释放资源

    OutputStreamWriter out = ... 
    java.sql.Connection conn = ... 
    try { 
        ...
    } catch(SQLException sqlex) { 
        ...
    } catch(IOException ioex) { 
        ...
    } finally { 
        if (conn != null) { 
            try { 
                conn.close(); 
            } catch(SQLException sqlex2) { 
                ...
            } 
        } 
    
        if (out != null) { 
            try { 
                out.close(); 
            } catch(IOException ioex2) { 
            ...
            } 
        } 
    }
    
    

    不要将异常用作控制流(if-else)

    虽然异常本身具有流程控制的属性,但是直接作为类似 if-else这样的方式来使用还是不应该的

    public void check(String filePath) FileNotFoundException, FileExistException  {
        ...
    } 
    
    // 使用
    try {
        check();
    } catch (FileNotFoundException fne) {
        // do-file not found things ..
    } catch (FileExistException fee) {
        // do-file found things
    }
    
    

    不要忽略异常

    • 可以只打个日志,但是不要什么都不做
    • 如果你什么都做不了,就不要抓或者转换为RuntimeException
    try {
        Class.forName("com.mysql.jdbc.Driver");
    } catch (ClassNotFoundException ex) {} //忽略的异常,这样要不得。出了问题坑自己、坑队友
        
    // 接收异常
    try {
        ...
    } catch (Exception e) {  // 过分泛华的异常,不利于排查问题
        ...
    }
    
    // 抛出异常
    try {
        ...
    } catch (IOException ioe) {
        throw new Exception(ioe); // 泛化了异常, 外层调用丢失了异常类型的优势
    }
    
    // 自定义异常
    try {
        ...
    } catch (SqlException sqle) {
        throw new MyOwnException(); // 定义了新的异常,但是丢了原始异常信息
    }
    

    生产代码不要 printStackTrace();

    // 不好的方式
    try {
        ... 
    } catch (IOException e) {
        e.printStackTrace();
    }
    
    try {
        ...
    } catch (IOException e) {
        logger.error("message here" + e);
    }
    
    try {
    
    } catch (IOException e) {
        logger.error("message here" + e.getMessage());
    }
    
    // 比较好的方式
    try {
        ...
    } catch (IOException e) {
        logger.error("message here", e);
    }
    

    异常的捕获范围

    • 循环的场景,注意try代码块的范围
    // bad case
    try {
        while(rs.hasNext()) {
            foo(rs.next());
        }
    } catch (SomeException se) {
        ...
    }
    
    // good case
    while(rs.hasNext()) {
        try {
            foo(rs.next());
        } catch (SomeException se) {
            ...
        }
    } 
    

    参考资料

    http://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html

    http://stackoverflow.com/questions/6115896/java-checked-vs-unchecked-exception-explanation

    http://www.javapractices.com/topic/TopicAction.do?Id=129

    http://www.ibm.com/developerworks/cn/java/j-lo-exception/index.html

    http://www.blogjava.net/freeman1984/archive/2013/07/26/148850.html

    相关文章

      网友评论

        本文标题:Java编码中异常的使用建议

        本文链接:https://www.haomeiwen.com/subject/sahrpttx.html