关于java assertion
大部分轉(zhuǎn)載自參考資料:http://www.ibm.com/developerworks/cn/java/l-javaassertion/index.html
?
assertion(斷言)在軟件開(kāi)發(fā)中是一種常用的調(diào)試方式,assertion就是在程序中的一條語(yǔ)句,它對(duì)一個(gè)boolean表達(dá)式進(jìn)行檢查,一個(gè)正確程序必須保證這個(gè)boolean表達(dá)式的值為true;如果該值為false,說(shuō)明程序已經(jīng)處于不正確的狀態(tài)下,系統(tǒng)將給出警告或退出。一般來(lái)說(shuō),assertion用于保證程序最基本、關(guān)鍵的正確性。assertion檢查通常在開(kāi)發(fā)和測(cè)試時(shí)開(kāi)啟。為了提高性能,在軟件發(fā)布后,assertion檢查通常是關(guān)閉的。下面簡(jiǎn)單介紹一下Java中assertion的實(shí)現(xiàn)。
?
1.1 語(yǔ)法表示
在語(yǔ)法上,為了支持assertion,Java增加了一個(gè)關(guān)鍵字assert。它包括兩種表達(dá)式:
1)?assert expression1;
2)?assert expression1: expression2;
表達(dá)式中,expression1表示一個(gè)boolean表達(dá)式,expression2表示一個(gè)基本類型或者是一個(gè)Object的instance,基本類型包括boolean,char,double,float,int和long。由于所有類都為Object的子類,因此這個(gè)參數(shù)可以用于所有class的instance。
?
1.2 語(yǔ)義含義
在運(yùn)行時(shí),如果關(guān)閉了assertion功能,這些語(yǔ)句將不起任何作用。如果打開(kāi)了assertion功能,那么expression1的值將被計(jì)算,如果它的值為false,該語(yǔ)句將拋出一個(gè)AssertionError對(duì)象。如果assertion語(yǔ)句包括expression2參數(shù),程序?qū)⒂?jì)算出expression2的結(jié)果,然后將這個(gè)結(jié)果作為AssertionError的構(gòu)造函數(shù)的參數(shù),來(lái)創(chuàng)建AssertionError對(duì)象,并拋出該對(duì)象;如果expression1值為true,expression2將不被計(jì)算。
一種特殊情況是,如果在計(jì)算表達(dá)式時(shí),表達(dá)式本身拋出Exception,那么assert將停止運(yùn)行,而拋出這個(gè)Exception。
?
1.3 舉例
1.4 編譯
必須使用JDK1.4(或者更新)的Java編譯器
1.5 運(yùn)行
我們可以選擇開(kāi)啟assertion功能,或者不開(kāi)啟,另外我們還可以開(kāi)啟一部分類或包的assertion功能,通過(guò)這些選項(xiàng),我們可以過(guò)濾所有我們不關(guān)心的類,只選擇我們關(guān)心的類或包來(lái)觀察,下面介紹部分選項(xiàng)
-esa和?-dsa
含義為開(kāi)啟(關(guān)閉)系統(tǒng)類的assertion功能。由于新版本的Java的系統(tǒng)類中,也使了assertion語(yǔ)句,因此如果用戶需要觀察它們的運(yùn)行情況,就需要打開(kāi)系統(tǒng)類的assertion功能 ,我們可使用-esa參數(shù)打開(kāi),使用 -dsa參數(shù)關(guān)閉。 -esa和-dsa的全名為-enablesystemassertions和-disenablesystemassertions,全名和縮寫名有同樣的功能
-ea和?-ea
含義為開(kāi)啟(關(guān)閉)用戶類的assertion功能:通過(guò)這個(gè)參數(shù),用戶可以打開(kāi)某些類或包的assertion功能,同樣用戶也可以關(guān)閉某些類和包的assertion功能。打開(kāi)assertion功能參數(shù)為-ea;如果不帶任何參數(shù),表示打開(kāi)所有用戶類;如果帶有包名稱或者類名稱,表示打開(kāi)這些類或包;如果包名稱后面跟有三個(gè)點(diǎn),代表這個(gè)包及其子包;如果只有三個(gè)點(diǎn),代表無(wú)名包。關(guān)閉assertion功能參數(shù)為-da,使用方法與-ea類似。?-ea和-da的全名為-enableassertions和-disenableassertions,全名和縮寫名有同樣的功能
下面表格表示了參數(shù)及其含義,并有例子說(shuō)明如何使用:
?
其中...代表,此包和其子包的含義。例如我們有兩個(gè)包為pkg1和pkg1.subpkg。那么pkg1...就代表pkg1和pkg1.subpkg兩個(gè)包。?
另外,Java為了讓程序也能夠動(dòng)態(tài)開(kāi)啟和關(guān)閉某些類和包的assertion功能,Java修該了Class和ClassLoader的實(shí)現(xiàn),增加了幾個(gè)用于操作assert的API。下面簡(jiǎn)單說(shuō)明一下幾個(gè)API的作用。?
ClassLoader類中的幾個(gè)相關(guān)的API:?
setDefaultAssertionStatus:用于開(kāi)啟/關(guān)閉assertion功能?
setPackageAssertionStatus:用于開(kāi)啟/關(guān)閉某些包的assertion功能?
setClassAssertionStatus: 用于開(kāi)啟/關(guān)閉某些類的assertion功能?
clearAssertionStatus:用于關(guān)閉assertion功能
?
1.6 assertion的設(shè)計(jì)問(wèn)題
首先,我們認(rèn)為assertion是必要的。因?yàn)?#xff0c;如果沒(méi)有統(tǒng)一的assertion機(jī)制,Java程序通常使用if-then-else或者switch-case語(yǔ)句進(jìn)行assertion檢查,而且檢查的數(shù)據(jù)類型也不完全相同。assertion機(jī)制讓Java程序員用統(tǒng)一的方式處理assertion問(wèn)題,而不是按自己的方式處理。另外,如果用戶使用自己的方式進(jìn)行檢查,那么這些代碼在發(fā)布以后仍然將起作用,這可能會(huì)影響程序的性能。而從語(yǔ)言言層次支持assertion功能,這將把a(bǔ)ssertion對(duì)性能帶來(lái)的負(fù)面影響降到最小。
Java是通過(guò)增強(qiáng)一個(gè)關(guān)鍵字assert實(shí)現(xiàn)支持assertion,而不是使用一個(gè)庫(kù)函數(shù)支持,這說(shuō)明Java認(rèn)為assertion對(duì)于語(yǔ)言本身來(lái)說(shuō)是非常重要的。實(shí)際上,在Java的早期的規(guī)范中,Java是能夠支持assert的,但是由于一些實(shí)現(xiàn)的限制,這些特性從規(guī)范中除去了。因此,assert的再次引入應(yīng)該是恢復(fù)了Java對(duì)assert的支持。C語(yǔ)言就是通過(guò)Assert.h函數(shù)庫(kù)實(shí)現(xiàn)斷言的支持。
Java的assertion的開(kāi)啟也和C語(yǔ)言不太一樣,我們都知道在C語(yǔ)言中,assertion的開(kāi)啟是在編譯時(shí)候決定的。當(dāng)我們使用debug方式編譯程序時(shí)候,assertion被開(kāi)啟,而使用release方式編譯時(shí)候,assertion自動(dòng)被關(guān)閉。而Java的assertion卻是在運(yùn)行的時(shí)候進(jìn)行決定的。其實(shí),這兩種方式是各有優(yōu)缺點(diǎn)。如果采用編譯時(shí)決定方式,開(kāi)發(fā)人員將處理兩種類型的目標(biāo)碼,debug版本和release版本,這加大了文檔管理的難度,但是提高了代碼的運(yùn)行效率。Java采用運(yùn)行時(shí)決定的方式,這樣所有的assertion信息將置于目標(biāo)代碼中,同一目標(biāo)代碼可以選擇不同方式運(yùn)行,增強(qiáng)目標(biāo)代碼的靈活性,但是它將犧牲因?yàn)閍ssertion而引起一部分性能損失。Java專家小組認(rèn)為,所犧牲的性能相當(dāng)小,因此java采用了運(yùn)行時(shí)決定方式。
另外,我們注意到AssertionError作為Error的一個(gè)子類,而不是RuntimeException。關(guān)于這一點(diǎn),專家組也進(jìn)行了長(zhǎng)期的討論。Error代表一些異常的錯(cuò)誤,通常是不可以恢復(fù)的,而RuntimeException強(qiáng)調(diào)該錯(cuò)誤在運(yùn)行時(shí)才發(fā)生的特點(diǎn)。AssertionError通常為非常關(guān)鍵的錯(cuò)誤,這些錯(cuò)誤往往是不容易恢復(fù)的,而且assertion機(jī)制也不鼓勵(lì)程序員對(duì)這種錯(cuò)誤進(jìn)行恢復(fù)。因此,為了強(qiáng)調(diào)assertion的含義,Java專家小組選擇了讓AssertError為Error的子類。
?
1.7 assertion與繼承
考慮assertion與繼承的關(guān)系,研究assert是如何定位的。如果開(kāi)啟一個(gè)子類的assertion,那么它的父類的assertion是否執(zhí)行?
下面的例子將顯示如果一個(gè)assert語(yǔ)句在父類,而當(dāng)它的子類調(diào)用它時(shí),該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 }從這個(gè)例子我們可以看出,父類的assert語(yǔ)句將只有在父類的assert開(kāi)啟才起作用,如果僅僅開(kāi)啟子類的assert,父類的assert仍然不運(yùn)行。例如,我們執(zhí)行java -ea:Derived Derived的時(shí)候,Base類的assert語(yǔ)句并不執(zhí)行。因此,我們可以認(rèn)為,assert語(yǔ)句不具有繼承功能。
?
1.8?assertion的使用
assertion的使用是一個(gè)復(fù)雜的問(wèn)題,因?yàn)檫@將涉及到程序的風(fēng)格,assertion運(yùn)用的目標(biāo),程序的性質(zhì)等問(wèn)題。通常來(lái)說(shuō),assertion用于檢查一些關(guān)鍵的值,并且這些值對(duì)整個(gè)程序,或者局部功能的完成有很大的影響,并且這種錯(cuò)誤不容易恢復(fù)的。assertion表達(dá)式應(yīng)該短小、易懂,如果需要評(píng)估復(fù)雜的表達(dá)式,應(yīng)該使用函數(shù)計(jì)算。以下是一些使用assertion的情況的例子,這些方式可以讓java程序的可靠性更高。
1)?檢查控制流:在if-then-else和swith-case語(yǔ)句中,我們可以在不應(yīng)該發(fā)生的控制支流上加上assert false語(yǔ)句。如果這種情況發(fā)生了,assert能夠檢查出來(lái)。 例如: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)?在私有函數(shù)計(jì)算前,檢查輸入?yún)?shù)是否有效;對(duì)于一私有些函數(shù),要求輸入滿足一些特定的條件,那么我們可以在函數(shù)開(kāi)始處使用assert進(jìn)行參數(shù)檢查。對(duì)于公共函數(shù),我們通常不使用assertion檢查,因?yàn)橐话銇?lái)說(shuō),公共函數(shù)必須對(duì)無(wú)效的參數(shù)進(jìn)行檢查和處理。而私有函數(shù)往往是直接使用的。?
例如:某函數(shù)可能要求輸入的參數(shù)必須不為null。那么我們可以在函數(shù)的一開(kāi)始加上?assert parameter1!=null : "paramerter is null in test method";
3)?在函數(shù)計(jì)算后,檢查函數(shù)結(jié)果是否有效;對(duì)于一些計(jì)算函數(shù),函數(shù)運(yùn)行完成后,某些值需要保證一定的性質(zhì),因此我們可以通過(guò)assert檢查該值。 例如,我們有一個(gè)計(jì)算絕對(duì)值的函數(shù),那么我們就可以在函數(shù)的結(jié)果處,加上一個(gè)語(yǔ)句:
assert value >= 0 : "Value should be bigger than 0:" + value;通過(guò)這種方式,我們可以對(duì)函數(shù)計(jì)算完的結(jié)果進(jìn)行檢查。
4)?檢查程序不變量;有些程序中,存在一些不變量,在程序的運(yùn)行生命周期,這些不變量的值都是不變的。這些不變量可能是一個(gè)簡(jiǎn)單表達(dá)式,也可能是一個(gè)復(fù)雜的表達(dá)式。對(duì)于一些關(guān)鍵的不變量,我們可以通過(guò)assert進(jìn)行檢查。 例如,在一個(gè)財(cái)會(huì)系統(tǒng)中,公司的支出和收入必須保持一定的平衡關(guān)系,因此我們可以編寫一個(gè)表達(dá)式檢查這種平衡關(guān)系,如下表示:
private boolean isBalance() {// ... }在這個(gè)系統(tǒng)中,在一些可能影響這種平衡關(guān)系的方法的前后,我們都可以加上assert驗(yàn)證:?assert isBalance():"balance is destoried";
?
下面是搜集的一個(gè)網(wǎng)友對(duì)assertion的看法,摘錄此處,作為參考:
?
assert 有很大的用處
首先可以用在單元測(cè)試代碼中。junit侵入性是很強(qiáng)的,如果整個(gè)工程大量的代碼都使用了junit,就難以去掉或者是選擇另外一個(gè)框架。如果單元測(cè)試代碼很多,并且想復(fù)用這些單元測(cè)試案例,應(yīng)該選擇assert而不是junit,便于使用別的單元測(cè)試框架,比如TestNG。同理正式的功能代碼根本就不應(yīng)該出現(xiàn)Junit,應(yīng)該使用assert.
assert主要適合在基類,框架類,接口類,核心代碼類,工具類中。換言之,當(dāng)你的代碼的調(diào)用者是另外一個(gè)程序員寫得業(yè)務(wù)代碼,或者是另外一個(gè)子系統(tǒng)時(shí),就很有必要使用它。比如你做了一個(gè)快速排序的算法
public static List<int> quickSort(List<int> list){
? assert list != null;
? // 申請(qǐng)臨時(shí)空間
? //開(kāi)始排序
? for(int i : list){
? ? ? //
? }
}
這種情況下,如果不檢查傳入?yún)?shù)的正確性,會(huì)拋出一個(gè)莫名其妙的空指針錯(cuò)誤。你的調(diào)用者可能并不清楚你代碼的細(xì)節(jié),在一個(gè)系統(tǒng)的深處調(diào)試一個(gè)空指針錯(cuò)誤是很浪費(fèi)時(shí)間的。就應(yīng)該直接明確的告訴你的調(diào)用者是傳入的參數(shù)有問(wèn)題。否則他會(huì)懷疑你的代碼有BUG。使用assert可以避免兩個(gè)程序員之間互相指責(zé)對(duì)方寫的代碼有問(wèn)題。
assert適用那些你知道具體是什么錯(cuò)誤,你和你的調(diào)用者已經(jīng)約定應(yīng)該由你的調(diào)用者去排除或檢查的錯(cuò)誤。你通過(guò)一個(gè)斷言告訴你的調(diào)用者。assert不適用那些外部系統(tǒng)造成的錯(cuò)誤,比如用戶輸入數(shù)據(jù)的錯(cuò)誤,某個(gè)外部文件格式錯(cuò)誤。這些錯(cuò)誤不是你的調(diào)用者而是用戶造成的,甚至于不屬于異常,因?yàn)槌霈F(xiàn)輸入錯(cuò)誤和文件格式錯(cuò)誤是經(jīng)常的,這些錯(cuò)誤應(yīng)該由業(yè)務(wù)代碼去檢查。
assert比較適合于被頻繁調(diào)用的 基類,框架代碼,工具類,核心代碼,接口代碼中,這正是它在運(yùn)行時(shí)被去掉的原因。測(cè)試代碼應(yīng)該在測(cè)試階段開(kāi)啟-ea參數(shù),便于對(duì)系統(tǒng)深處的核心代碼做仔細(xì)的測(cè)試。
Java較少使用assert的原因是Java有很完整的OO體系,強(qiáng)制類型轉(zhuǎn)換出現(xiàn)得較少,所以不需要類似c那樣需要頻繁的檢查指針的類型是否正確,指針是否為空。同時(shí)Java也很少直接管理內(nèi)存或緩沖區(qū),所以不需要頻繁的檢查傳入的緩沖區(qū)是否為空或者是已經(jīng)越界。
但使用好assert有助于提高框架代碼的正確性和減少框架代碼的使用者的調(diào)試時(shí)間。
轉(zhuǎn)載于:https://www.cnblogs.com/qrlozte/archive/2013/03/20/2971361.html
總結(jié)
以上是生活随笔為你收集整理的关于java assertion的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Apache和Nginx的区别
- 下一篇: Swift WKWebView读取本地