1. 程式人生 > >Java斷言(assert)

Java斷言(assert)

一、概述 

在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能給你帶來多少好處,多少麻煩,這是我們選擇是否使用的的原則。 

以上僅僅代表我個人觀點,歡迎大家留言討論。

 

-----------------------------------------------------------------------------------

下面是一些Assert的例子:

assert 0 < value;
assert 0 < value:"value="+value;
assert ref != null:"ref doesn''t equal null";
assert isBalanced();

 

-----------------------------------------------------------------------------------

AssertinError類是Error的直接子類,因此代表程式出現了嚴重的錯誤,這種異常通常是不需要程式設計師使用catch語句捕捉的。

 

使用assert的準則:assert語句的作用是保證程式內部的一致性,而不是使用者與程式之間的一致性,所以不應用在保證命令列引數的正確性。可以用來保證傳遞給private方法引數的正確性。因為私有方法只是在類的內部被呼叫,因而是程式設計師可以控制的,我們可以預期它的狀態是正確和一致的。公有方法則不適用。此外,assert語句可用於檢查任何方法結束時狀態的正確性,及在方法的開始檢查相關的初始狀態 等等。

 

assert語句並不構成程式正常執行邏輯的一部分,時刻記住在執行時它們可能不會被執行。

 

-----------------------------------------------------------------------------------

兩類引數:
  引數 -esa和 -dsa:
  它們含義為開啟(關閉)系統類的assertion功能。由於新版本的Java的系統類中,也使了 assertion語句,因此如果使用者需要觀察它們的執行情況,就需要開啟系統類的assertion功能 ,我們可使用-esa引數開啟,使用 -dsa引數關閉。 -esa和-dsa的全名為-enablesystemassertions和-disenablesystemassertions,全名和縮寫名有同樣的功能。


  引數 -ea和 -ea:
  它們含義為開啟(關閉)使用者類的assertion功能:通過這個引數,使用者可以開啟某些類或包的assertion功能,同樣使用者也可以關閉某些類和包的assertion功能。開啟assertion功能引數為-ea;如果不帶任何引數,表示開啟所有使用者類;如果帶有包名稱或者類名稱,表示開啟這些類或包;如果包名稱後面跟有三個點,代表這個包及其子包;如果只有三個點,代表無名包。關閉 assertion功能引數為-da,使用方法與-ea類似。
  -ea和-da的全名為-enableassertions和-disenableassertions,全名和縮寫名有同樣的功能。
  下面表格表示了引數及其含義,並有例子說明如何使用。
  引數     例子                          說明
  -ea      java -ea                   開啟所有使用者類的assertion
  -da      java -da                   關閉所有使用者類的assertion
  -ea:     java -ea:MyClass1   開啟MyClass1的assertion
  -da:     java -da: MyClass1  關閉MyClass1的assertion
  -ea:     java -ea:pkg1          開啟pkg1包的assertion
  -da:     java -da:pkg1          關閉pkg1包的assertion
  -ea:...  java -ea:...               開啟預設包(無名包)的assertion
  -da:...  java -da:...               關閉預設包(無名包)的assertion
  -ea:...  java -ea:pkg1...       開啟pkg1包和其子包的assertion
  -da:...  java -da:pkg1...       關閉pkg1包和其子包的assertion
  -esa    java -esa                 開啟系統類的assertion
  -dsa    java -dsa                 關閉系統類的assertion

 

-----------------------------------------------------------------------------------

不要再public的方法裡面檢查引數是不是為null之類的操作,例如:

public int get(String s){
      assert s != null;
}


如果需要檢查也最好通過 if s = null  丟擲 NullPointerException來檢查。

 
不要用assert來檢查方法操作的返回值來判斷方法操作的結果,例如:

assert list.removeAll();這樣看起來好像沒有問題 但是想想如果assert 被disable呢,那樣他就不會被執行了,所以removeAll()操作就沒有被執行,可以這樣代替
boolean boo = list.removeAl();
assert boo;

 

-----------------------------------------------------------------------------------

另外,Java為了讓程式也能夠動態開啟和關閉某些類和包的assertion功能,Java修該了Class和ClassLoader的實現,增加了幾個用於操作assert的API。下面簡單說明一下幾個API的作用。
ClassLoader類中的幾個相關的API:
  setDefaultAssertionStatus:用於開啟/關閉assertion功能
  setPackageAssertionStatus:用於開啟/關閉某些包的assertion功能
  setClassAssertionStatus: 用於開啟/關閉某些類的assertion功能
  clearAssertionStatus:用於關閉assertion功能 

 

from: https://jiangzhengjun.iteye.com/blog/694886