目錄[隱藏]
概述
常見的斷言特性
斷言使用方式
啟用斷言
何時需要使用斷言
什麼地方不要使用斷言
C裡面的函數
[編輯本段]
概述
Assert - 斷言
編寫代碼時,我們總是會做出一些假設,斷言就是用於在代碼中捕捉這些假設,可以將斷言看作是異常處理的一種進階形式。斷言表示為一些布林運算式,程式員相信在程式中的某個特定點該運算式值為真。可以在任何時候啟用和禁用斷言驗證,因此可以在測試時啟用斷言,而在部署時禁用斷言。同樣,程式投入運行後,終端使用者在遇到問題時可以重新起用斷言。
使用斷言可以建立更穩定,品質更好且不易於出錯的代碼。當需要在一個值為FALSE時中斷當前操作的話,可以使用斷言。單元測試必須使用斷言(Junit/JunitX)。
除了類型檢查和單元測試外,斷言還提供了一種確定各種特性是否在程式中得到維護的極好的方法。
使用斷言使我們向按契約式設計更近了一步。
[編輯本段]
常見的斷言特性
前置條件斷言:代碼執行之前必須具備的特性
後置條件斷言:代碼執行之後必須具備的特性
前後不變斷言:代碼執行前後不能變化的特性
[編輯本段]
斷言使用方式
斷言可以有兩種形式
1.assert Expression1
2.assert Expression1:Expression2
其中Expression1應該總是一個布爾值,Expression2是宣告失敗時輸出的失敗訊息的字串。如果Expression1為假,則拋出一個 AssertionError,這是一個錯誤,而不是一個異常,也就是說是一個不可控制異常(unchecked Exception),AssertionError由於是錯誤,所以可以不捕獲,但不推薦這樣做,因為那樣會使你的系統進入不穩定點。
[編輯本段]
啟用斷言
斷言在預設情況下是關閉的,要在編譯時間啟用斷言,需要使用source1.4標記 既javac source1.4 Test.java ,在運行時啟用斷言需要使用 -ea參數 。要在系統類別中啟用和禁用斷言可以使用 -esa 和 -dsa參數。
例如:
public class AssertExampleOne{
public AssertExampleOne(){}
public static void main(String args[]){
int x=10;
System.out.println("Testing Assertion that x==100");
assert x=100:"Out assertion failed!";
System.out.println("Test passed!");
}
}
如果編譯時間未加 -source1.4,則編譯通不過
在執行時未加 -ea 時輸出為
Testing Assertion that x==100
Test passed
jre忽略了斷言的就代碼,而使用了該參數就會輸出為
Testing Assertion that x==100
Exception in thread "main" java.lang.AssertionError: Out assertion failed!
at AssertExampleOne.main(AssertExampleOne.java:6)
斷言的副作用
由於程式員的問題,斷言的使用可能會帶來副作用 ,例如:
boolean isEnable=false;
//...
assert isEnable=true;
這個斷言的副作用是因為它修改了程式中變數的值並且未拋出錯誤,這樣的錯誤如果不細心的檢查是很難發現的。但是同時我們可以根據以上的副作用得到一個有用的特性,根據它來測試斷言是否開啟。
public class AssertExampleTwo{
public static void main(String args[]){
boolean isEnable=false;
//...
assert isEnable=true;
if(isEnable==false){
throw new RuntimeException("Assertion shoule be enable!");
}
}
}
[編輯本段]
何時需要使用斷言
1.可以在預計正常情況下程式不會到達的地方放置斷言 :assert false
2.斷言可以用於檢查傳遞給私人方法的參數。(對於公有方法,因為是提供給外部的介面,所以必須在方法中有相應的參數檢驗才能保證代碼的健壯性)
3.使用斷言測試方法執行的前置條件和後置條件
4.使用斷言檢查類的不變狀態,確保任何情況下,某個變數的狀態必須滿足。(如age屬性應大於0小於某個合適值)
[編輯本段]
什麼地方不要使用斷言
Assert 陳述式不是永遠會執行,可以屏蔽也可以啟用
因此:
1.不要使用斷言作為公用方法的參數檢查,公用方法的參數永遠都要執行
2.Assert 陳述式不可以有任何邊界效應,不要使用Assert 陳述式去修改變數和改變方法的傳回值
[編輯本段]
C裡面的函數
函數名: assert
功 能: 測試一個條件並可能使程式終止
用 法: void assert(int test);
程式例:
#include assert.h>
#include
#include
struct ITEM {
int key;
int value;
};
/* add item to list, make sure list is not null */
void additem(struct ITEM *itemptr) {
assert(itemptr != NULL);
/* add item to list */
}
int main(void)
{
additem(NULL);
return 0;
}
assert() 函數用法
assert宏的原型定義在中,其作用是如果它的條件返回錯誤,則終止程式執行,原型定義:
#include
void assert( int expression );
assert的作用是現計算運算式 expression ,如果其值為假(即為0),那麼它先向stderr列印一條出錯資訊,
然後通過調用 abort 來終止程式運行。
請看下面的程式清單badptr.c:
#include
#include
#include
int main( void )
{
FILE *fp;
fp = fopen( "test.txt", "w" );//以可寫的方式開啟一個檔案,如果不存在就建立一個同名檔案
assert( fp ); //所以這裡不會出錯
fclose( fp );
fp = fopen( "noexitfile.txt", "r" );//以唯讀方式開啟一個檔案,如果不存在就開啟檔案失敗
assert( fp ); //所以這裡出錯
fclose( fp ); //程式永遠都執行不到這裡來
return 0;
}
[root@localhost error_process]# gcc badptr.c
[root@localhost error_process]# ./a.out
a.out: badptr.c:14: main: Assertion `fp' failed.
已放棄
使用assert的缺點是,頻繁的調用會極大的影響程式的效能,增加額外的開銷。
在調試結束後,可以通過在包含#include 的語句之前插入 #define NDEBUG 來禁用assert調用,範例程式碼如下:
#include
#define NDEBUG
#include
用法總結與注意事項:
1)在函數開始處檢驗傳入參數的合法性
如:
int resetBufferSize(int nNewSize)
{
//功能:改變緩衝區大小,
//參數:nNewSize 緩衝區新長度
//傳回值:緩衝區當前長度
//說明:保持原資訊內容不變 nNewSize
assert(nNewSize >= 0);
assert(nNewSize
...
}
2)每個assert只檢驗一個條件,因為同時檢驗多個條件時,如果宣告失敗,無法直觀的判斷是哪個條件失敗
不好: assert(nOffset>=0 && nOffset+nSize
好: assert(nOffset >= 0);
assert(nOffset+nSize
3)不能使用改變環境的語句,因為assert只在DEBUG個生效,如果這麼做,會使用程式在真正運行時遇到問題
錯誤: assert(i++
這是因為如果出錯,比如在執行之前i=100,那麼這條語句就不會執行,那麼i++這條命令就沒有執行。
正確: assert(i
i++;
4)assert和後面的語句應空一行,以形成邏輯和視覺上的一致感
5)有的地方,assert不能代替條件過濾
注意:當對於浮點數:
#include
// float pi=3.14;
// assert(pi=3.14); //
float pi=3.14f;
assert (pi=3.14f);
---------------------------------------------------------
在switch語句中總是要有default子句來顯示資訊(Assert)。
int number = SomeMethod();
switch(number)
{
case 1:
Trace.WriteLine("Case 1:");
break;
case 2:
Trace.WriteLine("Case 2:");
break;
default :
Debug.Assert(false);
break;
}