天天看點

[改善Java代碼]斷言絕對不是雞肋

建議19: 斷言絕對不是雞肋

在防禦式程式設計中經常會用斷言(Assertion)對參數和環境做出判斷,避免程式因不當的輸入或錯誤的環境而産生邏輯異常,斷言在很多語言中都存在,C、C++、Python都有不同的斷言表示形式。在Java中的斷言使用的是assert關鍵字,其基本的用法如下:

assert <布爾表達式> 
assert <布爾表達式> : <錯誤資訊>      

在布爾表達式為假時,抛出AssertionError錯誤,并附帶了錯誤資訊。assert的文法較簡單,有以下兩個特性:

(1)assert預設是不啟用的

我們知道斷言是為調試程式服務的,目的是為了能夠快速、友善地檢查到程式異常,但Java在預設條件下是不啟用的,要啟用就需要在編譯、運作時加上相關的關鍵字,這就不多說,有需要的話可以參考一下Java規範。

(2)assert抛出的異常AssertionError是繼承自Error的

斷言失敗後,JVM會抛出一個AssertionError錯誤,它繼承自Error,注意,這是一個錯誤,是不可恢複的,也就表示這是一個嚴重問題,開發者必須予以關注并解決之。

assert雖然是做斷言的,但不能将其等價于if…else…這樣的條件判斷,它在以下兩種情況不可使用:

(1)在對外公開的方法中

我們知道防禦式程式設計最核心的一點就是:所有的外部因素(輸入參數、環境變量、上下文)都是“邪惡”的,都存在着企圖摧毀程式的罪惡本源,為了抵制它,我們要在程式中處處檢驗,滿地設卡,不滿足條件就不再執行後續程式,以保護主程式的正确性,處處設卡沒問題,但就是不能用斷言做輸入校驗,特别是公開方法。我們來看一個例子:

1 public class Client {  
 2     public static void main(String[] args) {  
 3          StringUtils.encode(null);  
 4     }  
 5 }  
 6 //字元串處理工具類  
 7 class StringUtils{  
 8      public static String encode(String str){  
 9           assert str!=null:"加密的字元串為null";  
10           /*加密處理*/  
11     }  
12 }      

encode方法對輸入參數做了不為空的假設,如果為空,則抛出AssertionError錯誤,但這段程式存在一個嚴重的問題,encode是一個public方法,這标志着是它對外公開的,任何一個類隻要能夠傳遞一個String類型的參數(遵守契約)就可以調用,但是Client類按照規範和契約調用enocde方法,卻獲得了一個AssertionError錯誤資訊,是誰破壞了契約協定?—是encode方法自己。

(2)在執行邏輯代碼的情況下

assert的支援是可選的,在開發時可以讓它運作,但在生産系統中則不需要其運作了(以便提高性能),是以在assert的布爾表達式中不能執行邏輯代碼,否則會因為環境不同而産生不同的邏輯,例如:

1 public void doSomething(List list,Object element){  
2      assert list.remove(element):"删除元素 " + element + " 失敗";  
3      /*業務處理*/  
4 }      

這段代碼在assert啟用的環境下,沒有任何問題,但是一旦投入到生産環境,就不會啟用斷言了,而這個方法也就徹底完蛋了,list的删除動作永遠都不會執行,是以也就永遠不會報錯或異常,因為根本就沒有執行嘛!

以上兩種情況下不能使用assert,那在什麼情況下能夠使用assert呢?一句話:按照正常執行邏輯不可能到達的代碼區域可以放置assert。具體分為三種情況:

(1)在私有方法中放置assert作為輸入參數的校驗

在私有方法中可以放置assert校驗輸入參數,因為私有方法的使用者是作者自己,私有方法的調用者和被調用者之間是一種弱契約關系,或者說沒有契約關系,其間的限制是依靠作者自己控制的,是以加上assert可以更好地預防自己犯錯,或者無意的程式犯錯。

(2)流程控制中不可能達到的區域

這類似于JUnit的fail方法,其标志性的意義就是:程式執行到這裡就是錯誤的,例如:

1 public void doSomething(){  
2      int i = 7;  
3      while(i >7){  
4         /*業務處理*/  
5      }  
6      assert false:"到達這裡就表示錯誤";  
7 }      

(3)建立程式探針

我們可能會在一段程式中定義兩個變量,分别代表兩個不同的業務含義,但是兩者有固定的關系,例如var1=var2*2,那我們就可以在程式中到處設“樁”,斷言這兩者的關系,如果不滿足即表明程式已經出現了異常,業務也就沒有必要運作下去了。

//===========================================================================

Eclipse中預設是關閉斷言assert的.怎麼樣在eclipse中開啟斷言...

最簡單的方式:

部落格園對上傳的圖檔有壓縮,顯示不清楚,點選看高清大圖:javascript:void(0)

[改善Java代碼]斷言絕對不是雞肋
作者:SummerChill