1.6 要決斷:使用Java斷言
Java5+
“編程人員總是正確的—— 是編譯器和解釋器造成的錯誤。”我確信你認同這種說法。作為編程人員,經常要對變量的值做出假設并且基于此編寫代碼。盡管非常不愿意承認可能在設計或實現上有錯誤,但有時變量和參數卻沒有獲得期望的值。
當設計和編寫代碼時,只有在最初的假設仍然成立的情況下代碼才能正確運行。如果沒有任何有關這些假設的聲明,那么閱讀代碼的任何人(甚至你自己)都不清楚它們的含義是什么。從而導致今后的改動可能會違反這些假設并引入難以查找的錯誤。通常,在注釋中說明假設能使以后修改代碼的人避免出錯。
使用注釋來說明假設是一個好的開始。但是當出現違反假設的情況時,程序有時會繼續運行就像未出現任何問題一樣。一些情況下,開發人員能夠馬上看到結果,并且可以糾正出現的問題。但在另一些情況下,存在一個潛伏的錯誤時,可能會對應用程序的其他部分造成負面影響,對于分布式系統而言,則可能會對完全不同的另一個應用程序造成負面影響!跟蹤這樣的問題非常困難。
Java 1.4在語言中添加了斷言特性來簡化測試和調試,加強文檔編制并提高基于Java的可維護性。可以使用一個布爾表達式來創建一個斷言,以便測試有關系統的當前狀態所假定的某些情況。如果斷言失敗,運行庫會拋出一個AssertionError。下面給出一個很簡單的斷言:
String name = "Brian"; |
這里,可以確定在給name分配了值“Brian”后它的值將不會為空。如果它的值為空,則出現了某種嚴重的錯誤!此斷言是當時在程序中對變量的值所做的假定的聲明。為如此簡單的例子做這種聲明看似可笑和多余。但是,當多個方法會影響一個對象的狀態時,這種方法是有效的。在下面的示例中,示例了這樣一個斷言,即在向新員工分配任何任務之前必須已經指派了一名管理人員。
Employee worker = |
通常,在對某個對象執行關鍵操作時會需要對它創建斷言。這有助于增強代碼的健壯性,比如如果在程序中出現了某種錯誤,可以更方便地調試程序。這樣做要比程序在某處執行失敗造成不良后果來發現錯誤要好得多。當知道程序失敗是由于它違反了假設而引起的時候,跟蹤失敗的原因要簡單得多。在這個代碼樣例中,使用了assert選項來返回更有用的信息。沒有此選項時,除了行號之外將無法得到有關斷言的標識信息。
在某些版本的編譯器上,當編譯源代碼時需要使用一個命令選項來設置編譯器的源兼容性模式(取決于編譯器的版本,如1.4或1.5)。
javac -source 1.5 MyClass.java |
現在強制斷言失敗并觀察會出現什么情況:
public class AssertBad { |
默認情況下,運行時環境不支持斷言,必須使用ea(允許斷言)命令選項來啟動JRE。上面的代碼會引起以下的結果:
C:\projects\wcj1> java -ea AssertBad |
要記住斷言是用來對那些不應該出現的情況進行實際的“健全性檢查”,因此不應該使用它們來替代常規的錯誤檢查。
警告:
不要讓斷言語句更改代碼中的狀態/值。否則當最終關閉斷言時,代碼的行為方式將不同于啟用斷言時代碼的行為。例如,不要創建如下的斷言:
assert (++i > 10); // BAD: i changes only with assertions enabled! |
通常,在整個開發階段都會啟用斷言。一旦完全測試了系統并將它移送到產品環境時,則希望禁用斷言,因為這樣做會略微改善性能。但是不要改動代碼來完成此操作,并且也不要刪除斷言。不管怎樣,為了編制文檔的目的,斷言也應保留在代碼中。這樣,當以后更改代碼時,會提醒程序員要保持所有假設都是有效的,并且這也是可測試的。