準確而細緻的錯誤處理是確保程式健壯性的重要關卡。 --------- 引
很容易分清正常和錯誤的區別,但不容易分辨錯誤和異常的區別。 事實上,從事情機率上可以很容易地分辨。 無論是正常、錯誤或異常,都是事情發生的一種可能,只是通常正常情形發生的機率更多,錯誤次之,異常很小。 但小機率事件也會發生。
舉例來說,寫一個與天氣有關的程式。 天氣有晴天、陰天、雨天、雪天,偶爾會有酸雨天,還有既晴又雨的時候。 晴天、陰天和雨天、雪天都可以看成正常情況, 但酸雨天就是一種錯誤情形,因為是不期望的;既晴且雨的天氣就更少發生了,可以看作一種異常,是人們未曾預料到的。 想想最初的那個BUG, 據說是飛蟲卡在機器裡了, 這是一種預料之外的事件,可稱為異常。 而預料之內的事情,可以包括正常和錯誤。
總結一下: 正常和錯誤都屬於預料之內的事情, 發生機率較大; 異常通常是預料之外的,發生機率極小,但也可能發生。 正常與錯誤的區別在於,一個是預期的,一個是不期望的。
PS: 有人會問: 那麼發生機率很小但卻是期望的事情是什麼呢? ------ 那當然是 Surprise 啦!
明確正常、錯誤和異常的共同點和區別對於編寫健壯性好的程式是必不可少的。通常,我們更注意處理正常情況,那被看作是一種必須完成的“功能或特色”,是最起碼的要求; 錯誤是不期望的,但也是必須處理的。異常是不曾預料的,是必須做一手防備的。 正常情形通常是程式的主控制流程, 錯誤情形作為程式的分支, 而異常處理則作為一種高層戒備。 程式的結構基本是這樣的:
String result = DEFAULT_VALUE;try { doSome1(); // 正常流 if (error) { // 錯誤情形的分支處理,相當於關卡 return ERROR_INFO; } return result; }catch (SomeException se) { // 異常情形 logger.error(e); return EXCEPTION_INFO;}finally { cleanup();}
1. 初始值要認真考慮,不要隨手設定。 最好設定為預設值。
2. try {} 塊裡麵包含了正常和錯誤情形的處理, 正常情形是主控制流程, 錯誤情形是分支處理(既然預料到了,就必須加以處理了)。 異常則在 try 之外, 因為是預料不到,要防一手。
3. 無論底層會出現什麼錯誤, 高層總是會返回一個合理的值。所有的情況,包括異常也被處理了。
4. 使用衛述句式。錯誤情形處理總是會導致一大堆 if-else , 降低程式可讀性。對比一下:
if (正常情形) { doNormal(); } VS: if(someError) { doSomeError(); return SOME_ERROR_INFO1 };
elseif(SomeError) {doSomeError();} if(someError2) { doSomeError2(); return SOME_ERROR_INFO2 };
else {doSomeError2(); } // continue normal doing.
反正我更傾向與後者呈現的結構。
5. 出錯了總是會返回某種資訊,交由高層處理。 總有一層能夠合理處理。可以返回預設值,拋出異常,或交由更高一層, 或者什麼都不做,直接忽略。 總要採取一種措施。
6. 由於錯誤涉及到底層到高層的傳遞,甚至傳遞不只一層, 因此, 是一個整體構架的問題。 應該從整體上仔細設計。