Morse Code

异常处理

近期接触新的项目,发现原有异常设计是这样的,每个层抛出自己的 checked exception (可能包含异常信息,而且信息的格式五花八门)。 如分为4层 Action -> Service -> Biz -> DAO,异常结构是 Action 捕获 ServiceException(BizException(DAOException("insert db error"))),然后给用户展示 e.getMessage()

虽然觉得这么做不好,但是为什么不好呢。。


1.《Effective Java》中关于异常处理的几条建议

1). 只针对不正常的情况才使用异常

不要用异常来控制流程。如:跳出循环

2). 对于可恢复的条件使用被检查的异常,对于程序错误使用运行时异常

3). 避免不必要的使用被检查的异常

如果用checked exception需要同时满足以下两点: 1. 即使正确使用API并不能阻止异常条件的发生。 2. 产生了异常,使用API的程序员可以采取有用的动作对程序进行处理。

4). 尽量使用标准的异常

异常名 使用场景
IllegalArgumentException 参数的值不合适
IllegalStateException 参数的状态不合适
UnsupportedOperationException 对象不支持客户请求的方法
... ...
#### 5). 抛出的异常要适合于相应的抽象
在Java的集合框架AbstractSequentialList的get()方法如下:
public E get(int index) {
    try {
        return listIterator(index).next();
    } catch (NoSuchElementException exc) {
        throw new IndexOutOfBoundsException("Index: "+index);
    }
}

listIterator(index)会返回ListIterator对象,调用该对象的next()方法可能会抛出NoSuchElementException异常。而在get()方法中,抛出NoSuchElementException异常会让人感到困惑。所以,get()对NoSuchElementException进行了捕获,并抛出了IndexOutOfBoundsException异常。即,相当于将NoSuchElementException转译成了IndexOutOfBoundsException异常。

6). 每个方法抛出的异常都要有文档

7). 在细节消息中包含失败 -- 捕获消息

8). 不要忽略异常


2. exception 真的性能好低

exception 继承与 throwable,里面有个fillInStackTrace,这个方法的定义:

public synchronized native Throwable fillInStackTrace();   

jdk的具体实现:

    Java_java_lang_Throwable_fillInStackTrace(JNIEnv *env, jobject throwable)  
    {  
        JVM_FillInStackTrace(env, throwable);  
        return throwable;  
    }  

    JVM_ENTRY(void, JVM_FillInStackTrace(JNIEnv *env, jobject receiver))  
      JVMWrapper("JVM_FillInStackTrace");  
      Handle exception(thread, JNIHandles::resolve_non_null(receiver));  
      java_lang_Throwable::fill_in_stack_trace(exception);  
    JVM_END  

    void java_lang_Throwable::fill_in_stack_trace(Handle throwable, TRAPS) {  
      if (!StackTraceInThrowable) return;  
      ResourceMark rm(THREAD);  

      ……………………………………………….  
      BacktraceBuilder bt(CHECK);  
      ………………………………………………  
      for (frame fr = thread->last_frame(); max_depth != total_count;) {  
        methodOop method = NULL;  
        int bci = 0;  
      ………………………………………………  
        bt.push(method, bci, CHECK);  
        total_count++;  
      }  

      // Put completed stack trace into throwable object  
      set_backtrace(throwable(), bt.backtrace());  
    }  

上面的代码中,这一系列调用可以发现,当你new一个exception的时候,jvm已经在exception里构建好了所有的stacktrace(BacktraceBuilderbt),这里花费的代价是可观的,试想一下,在web项目中,调用栈的深度可是很大的。因此,当你对stacktrace不感兴趣的时候,不需要这样的信息时,最好不要随便的new exception。


总结1和2,虽然现在的系统异常结构不好,修改量过大,复写了fillInStackTrace,直接返回null,对程序无影响。