Java陷阱之a(chǎn)ssert關(guān)鍵字詳解
一、概述
在C和C++語(yǔ)言中都有assert關(guān)鍵,表示斷言。
在Java中,同樣也有assert關(guān)鍵字,表示斷言,用法和含義都差不多。
二、語(yǔ)法
在Java中,assert關(guān)鍵字是從JAVA SE 1.4 引入的,為了避免和老版本的Java代碼中使用了assert關(guān)鍵字導(dǎo)致錯(cuò)誤,Java在執(zhí)行的時(shí)候默認(rèn)是不啟動(dòng)斷言檢查的(這個(gè)時(shí)候,所有的斷言語(yǔ)句都 將忽略?。?,如果要開啟斷言檢查,則需要用開關(guān)-enableassertions或-ea來(lái)開啟。
assert關(guān)鍵字語(yǔ)法很簡(jiǎn)單,有兩種用法:
1、assert <boolean表達(dá)式>
如果<boolean表達(dá)式>為true,則程序繼續(xù)執(zhí)行。
如果為false,則程序拋出AssertionError,并終止執(zhí)行。
2、assert <boolean表達(dá)式> : <錯(cuò)誤信息表達(dá)式>
如果<boolean表達(dá)式>為true,則程序繼續(xù)執(zhí)行。
如果為false,則程序拋出java.lang.AssertionError,并輸入<錯(cuò)誤信息表達(dá)式>。
三、應(yīng)用實(shí)例
下面給出一個(gè)例子,通過(guò)例子說(shuō)明其用法:
public class AssertFoo {
public static void main(String args[]) {
//斷言1結(jié)果為true,則繼續(xù)往下執(zhí)行
assert true;
System.out.println("斷言1沒有問(wèn)題,Go!");
System.out.println("\n-----------------\n");
//斷言2結(jié)果為false,程序終止
assert false : "斷言失敗,此表達(dá)式的信息將會(huì)在拋出異常的時(shí)候輸出!";
System.out.println("斷言2沒有問(wèn)題,Go!");
}
}
保存代碼到C:\AssertFoo.java,然后按照下面的方式執(zhí)行,查看控制臺(tái)輸出結(jié)果:
1、編譯程序:
C:\>javac AssertFoo.java
2、默認(rèn)執(zhí)行程序,沒有開啟-ea開關(guān):
C:\>java AssertFoo
斷言1沒有問(wèn)題,Go!
-----------------
斷言2沒有問(wèn)題,Go!
3、開啟-ea開關(guān),執(zhí)行程序:
C:\>java -ea AssertFoo
斷言1沒有問(wèn)題,Go!
-----------------
Exception in thread "main" java.lang.AssertionError: 斷言失敗,此表達(dá)式的信息將
會(huì)在拋出異常的時(shí)候輸出!
at AssertFoo.main(AssertFoo.java:10)
四、陷阱
assert關(guān)鍵字用法簡(jiǎn)單,但是使用assert往往會(huì)讓你陷入越來(lái)越深的陷阱中。應(yīng)避免使用。筆者經(jīng)過(guò)研究,總結(jié)了以下原因:
1、assert關(guān)鍵字需要在運(yùn)行時(shí)候顯式開啟才能生效,否則你的斷言就沒有任何意義。而現(xiàn)在主流的Java IDE工具默認(rèn)都沒有開啟-ea斷言檢查功能。這就意味著你如果使用IDE工具編碼,調(diào)試運(yùn)行時(shí)候會(huì)有一定的麻煩。并且,對(duì)于Java Web應(yīng)用,程序代碼都是部署在容器里面,你沒法直接去控制程序的運(yùn)行,如果一定要開啟-ea的開關(guān),則需要更改Web容器的運(yùn)行配置參數(shù)。這對(duì)程序的移 植和部署都帶來(lái)很大的不便。
2、用assert代替if是陷阱之二。assert的判斷和if語(yǔ)句差不多,但兩者的作用有著本質(zhì)的區(qū)別:assert關(guān)鍵字本意上是為測(cè)試 調(diào)試程序時(shí)使用的,但如果不小心用assert來(lái)控制了程序的業(yè)務(wù)流程,那在測(cè)試調(diào)試結(jié)束后去掉assert關(guān)鍵字就意味著修改了程序的正常的邏輯。
3、assert斷言失敗將面臨程序的退出。這在一個(gè)生產(chǎn)環(huán)境下的應(yīng)用是絕不能容忍的。一般都是通過(guò)異常處理來(lái)解決程序中潛在的錯(cuò)誤。但是使用斷言就很危險(xiǎn),一旦失敗系統(tǒng)就掛了。
五、對(duì)assert的思考
assert既然是為了調(diào)試測(cè)試程序用,不在正式生產(chǎn)環(huán)境下用,那應(yīng)該考慮更好的測(cè)試JUint來(lái)代替其做用,JUint相對(duì)assert關(guān)鍵的所提供的功能是有過(guò)之而無(wú)不及。當(dāng)然完全可以通過(guò)IDE debug來(lái)進(jìn)行調(diào)試測(cè)試。在此看來(lái),assert的前途一片昏暗。
因此,應(yīng)當(dāng)避免在Java中使用assert關(guān)鍵字,除非哪一天Java默認(rèn)支持開啟-ea的開關(guān),這時(shí)候可以考慮。對(duì)比一下,assert能給你帶來(lái)多少好處,多少麻煩,這是我們選擇是否使用的的原則。
============================================================
comment:
反過(guò)來(lái)說(shuō),在某些開源組件中,比如validator、junit中,判斷過(guò)程仿佛使用了斷言風(fēng)格,很有可能使用了大量的斷言,但筆者在沒看源碼之前不能確定。
如果是開發(fā)階段的簡(jiǎn)單測(cè)試,junit就是一個(gè)便捷強(qiáng)悍的工具,沒有理由自己寫斷言而不去用它。
============================================================
comment:
首先可以用在單元測(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í)空間
//開始排序
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è)試階段開啟-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í)間。
===============================================================
comment:
assert要達(dá)到的目的是讓程序員方便的發(fā)現(xiàn)自己的邏輯錯(cuò)誤,并且不影響程序的效率。assert所發(fā)現(xiàn)的錯(cuò)誤,是完全不應(yīng)該出現(xiàn)的,是不能用異常代替的。異常,那是系統(tǒng)所允許的,或者是系統(tǒng)不可控的“錯(cuò)誤”,它不是程序員的邏輯問(wèn)題。
assert應(yīng)該是開發(fā)階段打開,而在發(fā)布后關(guān)閉。
相關(guān)文章
springbooot整合dynamic?datasource數(shù)據(jù)庫(kù)密碼加密方式
這篇文章主要介紹了springbooot整合dynamic?datasource?數(shù)據(jù)庫(kù)密碼加密方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01JavaWeb實(shí)戰(zhàn)之開發(fā)網(wǎng)上購(gòu)物系統(tǒng)(超詳細(xì))
這篇文章主要介紹了JavaWeb實(shí)戰(zhàn)之開發(fā)網(wǎng)上購(gòu)物系統(tǒng)(超詳細(xì)),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有很好的幫助,需要的朋友可以參考下2021-04-04基于Redis實(shí)現(xiàn)分布式應(yīng)用限流的方法
本篇文章主要介紹了基于 Redis 實(shí)現(xiàn)分布式應(yīng)用限流的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12通過(guò)圖例了解IDEA引入JQuery實(shí)現(xiàn)步驟
這篇文章主要介紹了IDEA引入JQuery實(shí)現(xiàn)步驟圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09Java Json字符串的雙引號(hào)("")括號(hào)如何去掉
這篇文章主要介紹了Java Json字符串的雙引號(hào)("")括號(hào)如何去掉?具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09JAVA JNI函數(shù)的注冊(cè)過(guò)程詳細(xì)介紹
這篇文章主要介紹了JAVA JNI函數(shù)的注冊(cè)過(guò)程詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-11-11Java修改eclipse中web項(xiàng)目的server部署路徑問(wèn)題
這篇文章主要介紹了Java修改eclipse中web項(xiàng)目的server部署路徑,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11springboot配置開發(fā)和測(cè)試環(huán)境并添加啟動(dòng)路徑方式
這篇文章主要介紹了springboot配置開發(fā)和測(cè)試環(huán)境并添加啟動(dòng)路徑方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11