关于java assertion
大部分轉載自參考資料:http://www.ibm.com/developerworks/cn/java/l-javaassertion/index.html
?
assertion(斷言)在軟件開發中是一種常用的調試方式,assertion就是在程序中的一條語句,它對一個boolean表達式進行檢查,一個正確程序必須保證這個boolean表達式的值為true;如果該值為false,說明程序已經處于不正確的狀態下,系統將給出警告或退出。一般來說,assertion用于保證程序最基本、關鍵的正確性。assertion檢查通常在開發和測試時開啟。為了提高性能,在軟件發布后,assertion檢查通常是關閉的。下面簡單介紹一下Java中assertion的實現。
?
1.1 語法表示
在語法上,為了支持assertion,Java增加了一個關鍵字assert。它包括兩種表達式:
1)?assert expression1;
2)?assert expression1: expression2;
表達式中,expression1表示一個boolean表達式,expression2表示一個基本類型或者是一個Object的instance,基本類型包括boolean,char,double,float,int和long。由于所有類都為Object的子類,因此這個參數可以用于所有class的instance。
?
1.2 語義含義
在運行時,如果關閉了assertion功能,這些語句將不起任何作用。如果打開了assertion功能,那么expression1的值將被計算,如果它的值為false,該語句將拋出一個AssertionError對象。如果assertion語句包括expression2參數,程序將計算出expression2的結果,然后將這個結果作為AssertionError的構造函數的參數,來創建AssertionError對象,并拋出該對象;如果expression1值為true,expression2將不被計算。
一種特殊情況是,如果在計算表達式時,表達式本身拋出Exception,那么assert將停止運行,而拋出這個Exception。
?
1.3 舉例
1.4 編譯
必須使用JDK1.4(或者更新)的Java編譯器
1.5 運行
我們可以選擇開啟assertion功能,或者不開啟,另外我們還可以開啟一部分類或包的assertion功能,通過這些選項,我們可以過濾所有我們不關心的類,只選擇我們關心的類或包來觀察,下面介紹部分選項
-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,全名和縮寫名有同樣的功能
下面表格表示了參數及其含義,并有例子說明如何使用:
?
其中...代表,此包和其子包的含義。例如我們有兩個包為pkg1和pkg1.subpkg。那么pkg1...就代表pkg1和pkg1.subpkg兩個包。?
另外,Java為了讓程序也能夠動態開啟和關閉某些類和包的assertion功能,Java修該了Class和ClassLoader的實現,增加了幾個用于操作assert的API。下面簡單說明一下幾個API的作用。?
ClassLoader類中的幾個相關的API:?
setDefaultAssertionStatus:用于開啟/關閉assertion功能?
setPackageAssertionStatus:用于開啟/關閉某些包的assertion功能?
setClassAssertionStatus: 用于開啟/關閉某些類的assertion功能?
clearAssertionStatus:用于關閉assertion功能
?
1.6 assertion的設計問題
首先,我們認為assertion是必要的。因為,如果沒有統一的assertion機制,Java程序通常使用if-then-else或者switch-case語句進行assertion檢查,而且檢查的數據類型也不完全相同。assertion機制讓Java程序員用統一的方式處理assertion問題,而不是按自己的方式處理。另外,如果用戶使用自己的方式進行檢查,那么這些代碼在發布以后仍然將起作用,這可能會影響程序的性能。而從語言言層次支持assertion功能,這將把assertion對性能帶來的負面影響降到最小。
Java是通過增強一個關鍵字assert實現支持assertion,而不是使用一個庫函數支持,這說明Java認為assertion對于語言本身來說是非常重要的。實際上,在Java的早期的規范中,Java是能夠支持assert的,但是由于一些實現的限制,這些特性從規范中除去了。因此,assert的再次引入應該是恢復了Java對assert的支持。C語言就是通過Assert.h函數庫實現斷言的支持。
Java的assertion的開啟也和C語言不太一樣,我們都知道在C語言中,assertion的開啟是在編譯時候決定的。當我們使用debug方式編譯程序時候,assertion被開啟,而使用release方式編譯時候,assertion自動被關閉。而Java的assertion卻是在運行的時候進行決定的。其實,這兩種方式是各有優缺點。如果采用編譯時決定方式,開發人員將處理兩種類型的目標碼,debug版本和release版本,這加大了文檔管理的難度,但是提高了代碼的運行效率。Java采用運行時決定的方式,這樣所有的assertion信息將置于目標代碼中,同一目標代碼可以選擇不同方式運行,增強目標代碼的靈活性,但是它將犧牲因為assertion而引起一部分性能損失。Java專家小組認為,所犧牲的性能相當小,因此java采用了運行時決定方式。
另外,我們注意到AssertionError作為Error的一個子類,而不是RuntimeException。關于這一點,專家組也進行了長期的討論。Error代表一些異常的錯誤,通常是不可以恢復的,而RuntimeException強調該錯誤在運行時才發生的特點。AssertionError通常為非常關鍵的錯誤,這些錯誤往往是不容易恢復的,而且assertion機制也不鼓勵程序員對這種錯誤進行恢復。因此,為了強調assertion的含義,Java專家小組選擇了讓AssertError為Error的子類。
?
1.7 assertion與繼承
考慮assertion與繼承的關系,研究assert是如何定位的。如果開啟一個子類的assertion,那么它的父類的assertion是否執行?
下面的例子將顯示如果一個assert語句在父類,而當它的子類調用它時,該assert為false。我們看看在不同的情況下,該assertion是否被處理。
1 class Base { 2 public void baseMethod() { 3 assert false : "Assertion failed:This is base ";// 總是assertion失敗 4 System.out.println("Base Method"); 5 } 6 } 7 class Derived extends Base { 8 public void derivedMethod() { 9 assert false: "Assertion failed:This is derive";// 總是assertion失敗 10 System.out.println( "Derived Method" ); 11 } 12 public static void main( String[] args ) { 13 try { 14 Derived derived = new Derived(); 15 derived.baseMethod( ); 16 derived.derivedMethod(); 17 } 18 catch( AssertionError ae ) { 19 System.out.println(ae); 20 } 21 } 22 }從這個例子我們可以看出,父類的assert語句將只有在父類的assert開啟才起作用,如果僅僅開啟子類的assert,父類的assert仍然不運行。例如,我們執行java -ea:Derived Derived的時候,Base類的assert語句并不執行。因此,我們可以認為,assert語句不具有繼承功能。
?
1.8?assertion的使用
assertion的使用是一個復雜的問題,因為這將涉及到程序的風格,assertion運用的目標,程序的性質等問題。通常來說,assertion用于檢查一些關鍵的值,并且這些值對整個程序,或者局部功能的完成有很大的影響,并且這種錯誤不容易恢復的。assertion表達式應該短小、易懂,如果需要評估復雜的表達式,應該使用函數計算。以下是一些使用assertion的情況的例子,這些方式可以讓java程序的可靠性更高。
1)?檢查控制流:在if-then-else和swith-case語句中,我們可以在不應該發生的控制支流上加上assert false語句。如果這種情況發生了,assert能夠檢查出來。 例如:x取值只能使1,2,3,我們的程序可以如下表示:
1 switch (x) { 2 case 1: …; 3 case 2: …; 4 case 3: …; 5 default: assert false:"x value is invalid: "+x; 6 } 2)?在私有函數計算前,檢查輸入參數是否有效;對于一私有些函數,要求輸入滿足一些特定的條件,那么我們可以在函數開始處使用assert進行參數檢查。對于公共函數,我們通常不使用assertion檢查,因為一般來說,公共函數必須對無效的參數進行檢查和處理。而私有函數往往是直接使用的。?
例如:某函數可能要求輸入的參數必須不為null。那么我們可以在函數的一開始加上?assert parameter1!=null : "paramerter is null in test method";
3)?在函數計算后,檢查函數結果是否有效;對于一些計算函數,函數運行完成后,某些值需要保證一定的性質,因此我們可以通過assert檢查該值。 例如,我們有一個計算絕對值的函數,那么我們就可以在函數的結果處,加上一個語句:
assert value >= 0 : "Value should be bigger than 0:" + value;通過這種方式,我們可以對函數計算完的結果進行檢查?! ?/p>
4)?檢查程序不變量;有些程序中,存在一些不變量,在程序的運行生命周期,這些不變量的值都是不變的。這些不變量可能是一個簡單表達式,也可能是一個復雜的表達式。對于一些關鍵的不變量,我們可以通過assert進行檢查。 例如,在一個財會系統中,公司的支出和收入必須保持一定的平衡關系,因此我們可以編寫一個表達式檢查這種平衡關系,如下表示:
private boolean isBalance() {// ... }在這個系統中,在一些可能影響這種平衡關系的方法的前后,我們都可以加上assert驗證:?assert isBalance():"balance is destoried";
?
下面是搜集的一個網友對assertion的看法,摘錄此處,作為參考:
?
assert 有很大的用處
首先可以用在單元測試代碼中。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有助于提高框架代碼的正確性和減少框架代碼的使用者的調試時間。
轉載于:https://www.cnblogs.com/qrlozte/archive/2013/03/20/2971361.html
總結
以上是生活随笔為你收集整理的关于java assertion的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Apache和Nginx的区别
- 下一篇: Swift WKWebView读取本地