第二部分:流程控制,斷言和異常處理
第二部分:流程控制,斷言和異常處理
- 能夠正確使用if,switch語句,并且能正確使用合法的參數(shù)類型。
- 能夠正確使用所有帶標(biāo)簽或不帶標(biāo)簽的循環(huán)語句,能正確使用break,continue,能計(jì)算在循環(huán)中或循環(huán)后循環(huán)計(jì)數(shù)器的值。
- 能夠正確使用異常和異常處理語句(try,catch,finally)。能正確聲明拋出異常的方法,并知道怎樣覆蓋它。
- 了解代碼段中的異常對程序跳轉(zhuǎn)的影響。注意:異常可能是一個(gè)運(yùn)行時(shí)異常(runtime
exception),一個(gè)已經(jīng)定義的異常(checked exception),也可能是一個(gè)error。 - 能正確使用斷言,并了解關(guān)于斷言機(jī)制的正確說法。
- 能夠正確使用if,switch語句,并且能正確使用合法的參數(shù)類型。
- 能夠正確使用所有帶標(biāo)簽或不帶標(biāo)簽的循環(huán)語句,能正確使用break,continue,能計(jì)算在循環(huán)中或循環(huán)后循環(huán)計(jì)數(shù)器的值。
- 能夠正確使用異常和異常處理語句(try,catch,finally)。能正確聲明拋出異常的方法,并知道怎樣覆蓋它。
- 了解代碼段中的異常對程序跳轉(zhuǎn)的影響。注意:異常可能是一個(gè)運(yùn)行時(shí)異常(runtime
exception),一個(gè)已經(jīng)定義的異常(checked exception),也可能是一個(gè)error。 - 能正確使用斷言,并了解關(guān)于斷言機(jī)制的正確說法。
第一節(jié) 斷言 assert
§1.1.1 assertion 的語法和語義
J2SE 1.4在語言上提供了一個(gè)新特性,就是assertion(斷言)功能,它是該版本在Java語言方面最大的革新。在軟件開發(fā)中,assertion是一種經(jīng)典的調(diào)試、測試方式,本文將深入解析assertion功能的使用以及其設(shè)計(jì)理念,并給出相關(guān)的例子。
assertion(斷言)在軟件開發(fā)中是一種常用的調(diào)試方式,很多開發(fā)語言中都支持這種機(jī)制,如C,C++和Eiffel等,但是支持的形式不盡相同,有的是通過語言本身、有的是通過庫函數(shù)等。另外,從理論上來說,通過assertion方式可以證明程序的正確性,但是這是一項(xiàng)相當(dāng)復(fù)雜的工作,目前還沒有太多的實(shí)踐意義。
在實(shí)現(xiàn)中,assertion就是在程序中的一條語句,它對一個(gè)boolean表達(dá)式進(jìn)行檢查,一個(gè)正確程序必須保證這個(gè)boolean表達(dá)式的值為true;如果該值為false,說明程序已經(jīng)處于不正確的狀態(tài)下,系統(tǒng)將給出警告或退出。一般來說,assertion用于保證程序最基本、關(guān)鍵的正確性。assertion檢查通常在開發(fā)和測試時(shí)開啟。為了提高性能,在軟件發(fā)布后,assertion檢查通常是關(guān)閉的。下面簡單介紹一下Java中assertion的實(shí)現(xiàn)。
1.1) 語法表示
在語法上,為了支持assertion,Java增加了一個(gè)關(guān)鍵字assert。它包括兩種表達(dá)式,分別如下:
1.
2.
在兩種表達(dá)式中,expression_r1表示一個(gè)boolean表達(dá)式,expression_r2表示一個(gè)基本類型或者是一個(gè)對象(Object) ,基本類型包括boolean,char,double,float,int和long。由于所有類都為Object的子類,因此這個(gè)參數(shù)可以用于所有對象。
1.2) 語義含義
在運(yùn)行時(shí),如果關(guān)閉了assertion功能,這些語句將不起任何作用。如果打開了assertion功能,那么expression_r1的值將被計(jì)算,如果它的值為false,該語句強(qiáng)拋出一個(gè)AssertionError對象。如果assertion語句包括expression_r2參數(shù),程序?qū)⒂?jì)算出expression_r2的結(jié)果,然后將這個(gè)結(jié)果作為AssertionError的構(gòu)造函數(shù)的參數(shù),來創(chuàng)建AssertionError對象,并拋出該對象;如果expression_r1值為true,expression_r2將不被計(jì)算。
一種特殊情況是,如果在計(jì)算表達(dá)式時(shí),表達(dá)式本身拋出Exception,那么assert將停止運(yùn)行,而拋出這個(gè)Exception。
1.3) 一些assertion例子
下面是一些Assert的例子。
1.
2.
3.
4.
1.4) 編譯
由于assert是一個(gè)新關(guān)鍵字,使用老版本的JDK是無法編譯帶有assert的源程序。因此,我們必須使用JDK1.4(或者更新)的Java編譯器,在使用Javac命令時(shí),我們必須加上-source 1.4作為參數(shù)。-source 1.4表示使用JDK 1.4版本的方式來編譯源代碼,否則編譯就不能通過,因?yàn)槿笔〉腏avac編譯器使用JDK1.3的語法規(guī)則。
一個(gè)簡單的例子如下:
javac
1.5) 運(yùn)行
由于帶有assert語句的程序運(yùn)行時(shí),使用了新的ClassLoader和Class類,因此,這種程序必須在JDK1.4(或者更高版本)的JRE下運(yùn)行,而不能在老版本的JRE下運(yùn)行。
1.
§1.1.2 assertion 的設(shè)計(jì)問題
首先,我們認(rèn)為assertion是必要的。因?yàn)椋绻麤]有統(tǒng)一的assertion機(jī)制,Java程序通常使用if-then-else或者switch-case語句進(jìn)行assertion檢查,而且檢查的數(shù)據(jù)類型也不完全相同。assertion機(jī)制讓Java程序員用統(tǒng)一的方式處理assertion問題,而不是按自己的方式處理。另外,如果用戶使用自己的方式進(jìn)行檢查,那么這些代碼在發(fā)布以后仍然將起作用,這可能會影響程序的性能。而從語言言層次支持assertion功能,這將把a(bǔ)ssertion對性能帶來的負(fù)面影響降到最小。
Java是通過增強(qiáng)一個(gè)關(guān)鍵字assert實(shí)現(xiàn)支持assertion,而不是使用一個(gè)庫函數(shù)支持,這說明Java認(rèn)為assertion對于語言本身來說是非常重要的。實(shí)際上,在Java的早期的規(guī)范中,Java是能夠支持assert的,但是由于一些實(shí)現(xiàn)的限制,這些特性從規(guī)范中除去了。因此,assert的再次引入應(yīng)該是恢復(fù)了Java對assert的支持。C語言就是通過Assert.h函數(shù)庫實(shí)現(xiàn)斷言的支持。
Java的assertion的開啟也和C語言不太一樣,我們都知道在C語言中,assertion的開啟是在編譯時(shí)候決定的。當(dāng)我們使用debug方式編譯程序時(shí)候,assertion被開啟,而使用release方式編譯時(shí)候,assertion自動被關(guān)閉。而Java的assertion卻是在運(yùn)行的時(shí)候進(jìn)行決定的。其實(shí),這兩種方式是各有優(yōu)缺點(diǎn)。如果采用編譯時(shí)決定方式,開發(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)行了長期的討論。Error代表一些異常的錯(cuò)誤,通常是不可以恢復(fù)的,而RuntimeException強(qiáng)調(diào)該錯(cuò)誤在運(yùn)行時(shí)才發(fā)生的特點(diǎn)。AssertionError通常為非常關(guān)鍵的錯(cuò)誤,這些錯(cuò)誤往往是不容易恢復(fù)的,而且assertion機(jī)制也不鼓勵(lì)程序員對這種錯(cuò)誤進(jìn)行恢復(fù)。因此,為了強(qiáng)調(diào)assertion的含義,Java專家小組選擇了讓AssertError為Error的子類。
§1.1.3 assertion 與繼承
在本節(jié),我們將考慮assertion與繼承的關(guān)系,研究assert是如何定位的。如果開啟一個(gè)子類的assertion,那么它的父類的assertion是否執(zhí)行?
下面的例子將顯示如果一個(gè)assert語句在父類,而當(dāng)它的子類調(diào)用它時(shí),該assert為false。我們看看在不同的情況下,該assertion是否被處理。
class Base
{
}
class Derived
{
}
運(yùn)行命令 |
含義 |
結(jié)果 |
Java Derived |
不啟用assertion |
Base Method |
Java -ea Derived |
開啟所有assertion |
Java.lang.AssertionError:Assertion Failed:This is base |
Java -da Derived |
關(guān)閉所有assertion |
Base Method |
Java -ea:Base Derived |
僅打開Base的assertion |
Java.lang.AssertionError:Assertion Failed:This is base |
Java -ea:Derived Derived |
僅打開Derived的assertion |
Base Method |
從這個(gè)例子我們可以看出,父類的assert語句將只有在父類的assert開啟才起作用,如果僅僅開啟子類的assert,父類的assert仍然不運(yùn)行。例如,我們執(zhí)行java -ea:Derived Derived的時(shí)候,Base類的assert語句并不執(zhí)行。因此,我們可以認(rèn)為,assert語句不具有繼承功能。
§1.1.4 assertion 的使用
assertion的使用是一個(gè)復(fù)雜的問題,因?yàn)檫@將涉及到程序的風(fēng)格,assertion運(yùn)用的目標(biāo),程序的性質(zhì)等問題。通常來說,assertion用于檢查一些關(guān)鍵的值,并且這些值對整個(gè)程序,或者局部功能的完成有很大的影響,并且這種錯(cuò)誤不容易恢復(fù)的。assertion表達(dá)式應(yīng)該短小、易懂,如果需要評估復(fù)雜的表達(dá)式,應(yīng)該使用函數(shù)計(jì)算。以下是一些使用assertion的情況的例子,這些方式可以讓java程序的可靠性更高。
1.
例如:x取值只能使1,2,3,我們的程序可以如下表示
2.
3.
4.
5.
6.
7.
8.
例如:某函數(shù)可能要求輸入的參數(shù)必須不為null。那么我們可以在函數(shù)的一開始加上 assert parameter1!=null : "paramerter is null in test method";
9.
例如,我們有一個(gè)計(jì)算絕對值的函數(shù),那么我們就可以在函數(shù)的結(jié)果處,加上一個(gè)語句:
assert
通過這種方式,我們可以對函數(shù)計(jì)算完的結(jié)果進(jìn)行檢查。
10.
例如,在一個(gè)財(cái)會系統(tǒng)中,公司的支出和收入必須保持一定的平衡關(guān)系,因此我們可以編寫一個(gè)表達(dá)式檢查這種平衡關(guān)系,如下表示。
11.
12.
13.
在這個(gè)系統(tǒng)中,在一些可能影響這種平衡關(guān)系的方法的前后,我們都可以加上assert驗(yàn)證:assert isBalance():"balance is destoried";
§1.1.5 結(jié)論
assertion為開發(fā)人員提供了一種靈活地調(diào)試和測試機(jī)制,它的使用也非常簡單、方便。但是,如何規(guī)范、系統(tǒng)地使用assertion(特別是在Java語言中)仍然是一個(gè)亟待研究的問題
posted on 2009-10-16 11:38 李云澤 閱讀(349) 評論(0) 編輯 收藏 所屬分類: 面試筆試相關(guān)的 、SCJP認(rèn)證學(xué)習(xí)