一、概述
在C和C++語言中都有assert關鍵,表示斷言。
在Java中,同樣也有assert關鍵字,表示斷言,用法和意義都差不多。
二、語法
在Java中,assert關鍵字是從JAVA SE 1.4 引入的,為了避免和舊版的Java程式碼中使用了assert關鍵字導致錯誤,Java在執行的時候預設是不啟動斷言檢查的(這個時候,所有的斷言語句都將忽略! ),如果要開啟斷言檢查,則需要用開關-enableassertions或-ea來開啟。
assert關鍵字語法很簡單,有兩種用法:
1、assert <boolean表達式>
如果<boolean表達式>為true,則程式繼續執行。
如果為false,則程式拋出AssertionError,並終止執行。
2、assert <boolean表達式> : <錯誤訊息表達式>
如果<boolean表達式>為true,則程式繼續執行。
如果為false,則程式拋出java.lang.AssertionError,並輸入<錯誤訊息表達式>。
三、應用實例
下面舉例,透過例子說明其用法:
複製代碼代碼如下:
public class AssertFoo {
public static void main(String args[]) {
//斷言1結果為true,則繼續往下執行
assert true;
System.out.println("斷言1沒有問題,Go!");
System.out.println("/n-----------------/n");
//斷言2結果為false,程式終止
assert false : "斷言失敗,此表達式的資訊將會在拋出異常的時候輸出!";
System.out.println("斷言2沒有問題,Go!");
}
}
儲存程式碼到C:/AssertFoo.java,然後按照下面的方式執行,查看控制台輸出結果:
1、編譯程式:
C:/>javac AssertFoo.java
2、預設執行程序,沒有開啟-ea開關:
C:/>java AssertFoo
斷言1沒有問題,Go!
-----------------
斷言2沒有問題,Go!
3、開啟-ea開關,執行程式:
C:/>java -ea AssertFoo
斷言1沒有問題,Go!
-----------------
Exception in thread "main" java.lang.AssertionError: 斷言失敗,此表達式的資訊將
會在拋出異常的時候輸出!
at AssertFoo.main(AssertFoo.java:10)
四、陷阱
assert關鍵字用法簡單,但是使用assert往往會讓你陷入越來越深的陷阱。應避免使用。筆者經過研究,總結了以下原因:
1.assert關鍵字需要在運行時候明確開啟才能生效,否則你的斷言就沒有任何意義。而現在主流的Java IDE工具預設都沒有開啟-ea斷言檢查功能。這就代表你如果使用IDE工具編碼,調試運行時候會有一定的麻煩。而且,對於Java Web應用,程式碼都是部署在容器裡面,你沒辦法直接去控製程式的運行,如果一定要開啟-ea的開關,則需要更改Web容器的運行設定參數。這對程式的移植和部署都帶來很大的不便。
2.用assert代替if是陷阱之二。 assert的判斷和if語句差不多,但兩者的作用有著本質的區別:assert關鍵字本來是為測試調試程序時使用的,但如果不小心用assert來控制了程序的業務流程,那在測試調試結束後去掉assert關鍵字就意味著修改了程式的正常的邏輯。
3、assert斷言失敗將面臨程序的退出。這在一個生產環境下的應用是絕對不能容忍的。一般都是透過異常處理來解決程式中潛在的錯誤。但是使用斷言就很危險,一旦失敗系統就掛了。
五、對assert的思考
assert既然是為了調試測試程序用,不在正式生產環境下用,那應該考慮更好的測試JUint來代替其做用,JUint相對assert關鍵的所提供的功能是有過之而無不及。當然完全可以透過IDE debug來進行調試測試。在此看來,assert的前途一片昏暗。
因此,應避免在Java中使用assert關鍵字,除非哪一天Java預設支援開啟-ea的開關,這時候可以考慮。對比一下,assert能為你帶來多少好處,多少麻煩,這是我們選擇是否使用的原則。
==================================================== ==========
comment:
反過來說,在某些開源元件中,例如validator、junit中,判斷過程彷彿使用了斷言風格,很有可能使用了大量的斷言,但筆者在沒看源碼之前不能確定。
如果是開發階段的簡單測試,junit就是一個方便強悍的工具,沒有理由自己寫斷言而不去用它。
==================================================== ==========
comment:
首先可以用在單元測試程式碼中。 junit侵入性是很強的,如果整個工程大量的程式碼都使用了junit,就難以去掉或是選擇另一個框架。如果單元測試程式碼很多,並且想重複使用這些單元測試案例,應該選擇assert而不是junit,以便使用別的單元測試框架,例如TestNG。同理正式的功能代碼根本就不應該出現Junit,應該使用assert.
assert主要適合在基底類,框架類,介面類,核心程式碼類,工具類中。換言之,當你的程式碼的呼叫者是另一個程式設計師寫得業務代碼,或是另外一個子系統時,就很有必要使用它。例如你做了一個快速排序的演算法
複製代碼代碼如下:
public static List<int> quickSort(List<int> list){
assert list != null;
// 申請臨時空間
//開始排序
for(int i : list){
//
}
}
這種情況下,如果不檢查傳入參數的正確性,會拋出一個莫名其妙的空指標錯誤。你的呼叫者可能不清楚你程式碼的細節,在一個系統的深處調試一個空指標錯誤是很浪費時間的。就應該直接明確的告訴你的呼叫者是傳入的參數有問題。否則他會懷疑你的程式碼有BUG。使用assert可以避免兩個程式設計師之間互相指責對方寫的程式碼有問題。
assert適用那些你知道具體是什麼錯誤,你和你的呼叫者已經約定應該由你的呼叫者去排除或檢查的錯誤。你透過一個斷言告訴你的呼叫者。 assert不適用那些外部系統造成的錯誤,例如使用者輸入資料的錯誤,某個外部檔案格式錯誤。這些錯誤不是你的呼叫者而是使用者造成的,甚至於不屬於異常,因為出現輸入錯誤和檔案格式錯誤是經常的,這些錯誤應該由業務代碼去檢查。
assert比較適合於被頻繁調用的基類,框架代碼,工具類,核心代碼,接口代碼中,這正是它在運行時被去掉的原因。測試程式碼應該在測試階段開啟-ea參數,以便對系統深處的核心程式碼做仔細的測試。
Java較少使用assert的原因是Java有很完整的OO體系,強制型別轉換出現較少,所以不需要類似c那樣需要頻繁的檢查指針的型別是否正確,指針是否為空。同時Java也很少直接管理記憶體或緩衝區,所以不需要頻繁的檢查傳入的緩衝區是否為空或已經越界。
但使用好assert有助於提高框架程式碼的正確性和減少框架程式碼的使用者的偵錯時間。
==================================================== =============
comment:
assert要達到的目的是讓程式設計師方便的發現自己的邏輯錯誤,並且不影響程式的效率。 assert所發現的錯誤,是完全不該出現的,是不能用異常取代的。異常,那是系統所允許的,或者是系統不可控的“錯誤”,它不是程式設計師的邏輯問題。
assert應該是開發階段打開,而在發布後關閉。