?? chapter3.htm
字號:
注意若在預計為String值的地方使用,布爾值會自動轉換成適當的文本形式。<br>
在上述程序中,可將對int的定義替換成除boolean以外的其他任何主數據類型。但要注意,對浮點數字的比較是非常嚴格的。即使一個數字僅在小數部分與另一個數字存在極微小的差異,仍然認為它們是“不相等”的。即使一個數字只比零大一點點(例如2不停地開平方根),它仍然屬于“非零”值。<br>
<br>
1. 短路<br>
操作邏輯運算符時,我們會遇到一種名為“短路”的情況。這意味著只有明確得出整個表達式真或假的結論,才會對表達式進行邏輯求值。因此,一個邏輯表達式的所有部分都有可能不進行求值:<br>
<br>
107頁程序<br>
<br>
每次測試都會比較自變量,并返回真或假。它不會顯示與準備調用什么有關的資料。測試在下面這個表達式中進行:<br>
if(test1(0)) && test2(2) && test3(2))<br>
很自然地,你也許認為所有這三個測試都會得以執行。但希望輸出結果不至于使你大吃一驚:<br>
<br>
108頁程序<br>
<br>
第一個測試生成一個true結果,所以表達式求值會繼續下去。然而,第二個測試產生了一個false結果。由于這意味著整個表達式肯定為false,所以為什么還要繼續剩余的表達式呢?這樣做只會徒勞無益。事實上,“短路”一詞的由來正種因于此。如果一個邏輯表達式的所有部分都不必執行下去,那么潛在的性能提升將是相當可觀的。<br>
<br>
3.1.7 按位運算符<br>
按位運算符允許我們操作一個整數主數據類型中的單個“比特”,即二進制位。按位運算符會對兩個自變量中對應的位執行布爾代數,并最終生成一個結果。<br>
按位運算來源于C語言的低級操作。我們經常都要直接操縱硬件,需要頻繁設置硬件寄存器內的二進制位。Java的設計初衷是嵌入電視頂置盒內,所以這種低級操作仍被保留下來了。然而,由于操作系統的進步,現在也許不必過于頻繁地進行按位運算。<br>
若兩個輸入位都是1,則按位AND運算符(&)在輸出位里生成一個1;否則生成0。若兩個輸入位里至少有一個是1,則按位OR運算符(|)在輸出位里生成一個1;只有在兩個輸入位都是0的情況下,它才會生成一個0。若兩個輸入位的某一個是1,但不全都是1,那么按位XOR(^,異或)在輸出位里生成一個1。按位NOT(~,也叫作“非”運算符)屬于一元運算符;它只對一個自變量進行操作(其他所有運算符都是二元運算符)。按位NOT生成與輸入位的相反的值——若輸入0,則輸出1;輸入1,則輸出0。<br>
按位運算符和邏輯運算符都使用了同樣的字符,只是數量不同。因此,我們能方便地記憶各自的含義:由于“位”是非常“小”的,所以按位運算符僅使用了一個字符。<br>
按位運算符可與等號(=)聯合使用,以便合并運算及賦值:&=,|=和^=都是合法的(由于~是一元運算符,所以不可與=聯合使用)。<br>
我們將boolean(布爾)類型當作一種“單位”或“單比特”值對待,所以它多少有些獨特的地方。我們可執行按位AND,OR和XOR,但不能執行按位NOT(大概是為了避免與邏輯NOT混淆)。對于布爾值,按位運算符具有與邏輯運算符相同的效果,只是它們不會中途“短路”。此外,針對布爾值進行的按位運算為我們新增了一個XOR邏輯運算符,它并未包括在“邏輯”運算符的列表中。在移位表達式中,我們被禁止使用布爾運算,原因將在下面解釋。<br>
<br>
3.1.8 移位運算符<br>
移位運算符面向的運算對象也是二進制的“位”。可單獨用它們處理整數類型(主類型的一種)。左移位運算符(<<)能將運算符左邊的運算對象向左移動運算符右側指定的位數(在低位補0)。“有符號”右移位運算符(>>)則將運算符左邊的運算對象向右移動運算符右側指定的位數。“有符號”右移位運算符使用了“符號擴展”:若值為正,則在高位插入0;若值為負,則在高位插入1。Java也添加了一種“無符號”右移位運算符(>>>),它使用了“零擴展”:無論正負,都在高位插入0。這一運算符是C或C++沒有的。<br>
若對char,byte或者short進行移位處理,那么在移位進行之前,它們會自動轉換成一個int。只有右側的5個低位才會用到。這樣可防止我們在一個int數里移動不切實際的位數。若對一個long值進行處理,最后得到的結果也是long。此時只會用到右側的6個低位,防止移動超過long值里現成的位數。但在進行“無符號”右移位時,也可能遇到一個問題。若對byte或short值進行右移位運算,得到的可能不是正確的結果(Java
1.0和Java 1.1特別突出)。它們會自動轉換成int類型,并進行右移位。但“零擴展”不會發生,所以在那些情況下會得到-1的結果。可用下面這個例子檢測自己的實現方案:<br>
<br>
109-110頁程序<br>
<br>
移位可與等號(<<=或>>=或>>>=)組合使用。此時,運算符左邊的值會移動由右邊的值指定的位數,再將得到的結果賦回左邊的值。<br>
下面這個例子向大家闡示了如何應用涉及“按位”操作的所有運算符,以及它們的效果:<br>
<br>
110-112頁程序<br>
<br>
程序末尾調用了兩個方法:pBinInt()和pBinLong()。它們分別操作一個int和long值,并用一種二進制格式輸出,同時附有簡要的說明文字。目前,可暫時忽略它們具體的實現方案。<br>
大家要注意的是System.out.print()的使用,而不是System.out.println()。print()方法不會產生一個新行,以便在同一行里羅列多種信息。<br>
除展示所有按位運算符針對int和long的效果之外,本例也展示了int和long的最小值、最大值、+1和-1值,使大家能體會它們的情況。注意高位代表正負號:0為正,1為負。下面列出int部分的輸出:<br>
<br>
112-113頁程序<br>
<br>
數字的二進制形式表現為“有符號2的補值”。<br>
<br>
3.1.9 三元if-else運算符<br>
這種運算符比較罕見,因為它有三個運算對象。但它確實屬于運算符的一種,因為它最終也會生成一個值。這與本章后一節要講述的普通if-else語句是不同的。表達式采取下述形式:<br>
<br>
布爾表達式 ? 值0:值1<br>
<br>
若“布爾表達式”的結果為true,就計算“值0”,而且它的結果成為最終由運算符產生的值。但若“布爾表達式”的結果為false,計算的就是“值1”,而且它的結果成為最終由運算符產生的值。<br>
當然,也可以換用普通的if-else語句(在后面介紹),但三元運算符更加簡潔。盡管C引以為傲的就是它是一種簡練的語言,而且三元運算符的引入多半就是為了體現這種高效率的編程,但假若您打算頻繁用它,還是要先多作一些思量——它很容易就會產生可讀性極差的代碼。<br>
可將條件運算符用于自己的“副作用”,或用于它生成的值。但通常都應將其用于值,因為那樣做可將運算符與if-else明確區別開。下面便是一個例子:<br>
<br>
static int ternary(int i) {<br>
return i < 10 ? i * 100 : i * 10;<br>
}<br>
<br>
可以看出,假設用普通的if-else結構寫上述代碼,代碼量會比上面多出許多。如下所示:<br>
<br>
static int alternative(int i) {<br>
if (i < 10)<br>
return i * 100;<br>
return i * 10;<br>
}<br>
<br>
但第二種形式更易理解,而且不要求更多的錄入。所以在挑選三元運算符時,請務必權衡一下利弊。<br>
<br>
3.1.10 逗號運算符<br>
在C和C++里,逗號不僅作為函數自變量列表的分隔符使用,也作為進行后續計算的一個運算符使用。在Java里需要用到逗號的唯一場所就是for循環,本章稍后會對此詳加解釋。<br>
<br>
3.1.11 字串運算符+<br>
這個運算符在Java里有一項特殊用途:連接不同的字串。這一點已在前面的例子中展示過了。盡管與+的傳統意義不符,但用+來做這件事情仍然是非常自然的。在C++里,這一功能看起來非常不錯,所以引入了一項“運算符過載”機制,以便C++程序員為幾乎所有運算符增加特殊的含義。但非常不幸,與C++的另外一些限制結合,運算符過載成為一種非常復雜的特性,程序員在設計自己的類時必須對此有周到的考慮。與C++相比,盡管運算符過載在Java里更易實現,但迄今為止仍然認為這一特性過于復雜。所以Java程序員不能象C++程序員那樣設計自己的過載運算符。<br>
我們注意到運用“String +”時一些有趣的現象。若表達式以一個String起頭,那么后續所有運算對象都必須是字串。如下所示:<br>
<br>
int x = 0, y = 1, z = 2;<br>
String sString = "x, y, z ";<br>
System.out.println(sString + x + y + z);<br>
<br>
在這里,Java編譯程序會將x,y和z轉換成它們的字串形式,而不是先把它們加到一起。然而,如果使用下述語句:<br>
<br>
System.out.println(x + sString);<br>
<br>
那么早期版本的Java就會提示出錯(以后的版本能將x轉換成一個字串)。因此,如果想通過“加號”連接字串(使用Java的早期版本),請務必保證第一個元素是字串(或加上引號的一系列字符,編譯能將其識別成一個字串)。<br>
<br>
3.1.12 運算符常規操作規則<br>
使用運算符的一個缺點是括號的運用經常容易搞錯。即使對一個表達式如何計算有絲毫不確定的因素,都容易混淆括號的用法。這個問題在Java里仍然存在。<br>
在C和C++中,一個特別常見的錯誤如下:<br>
<br>
while(x = y) {<br>
//...<br>
}<br>
<br>
程序的意圖是測試是否“相等”(==),而不是進行賦值操作。在C和C++中,若y是一個非零值,那么這種賦值的結果肯定是true。這樣使可能得到一個無限循環。在Java里,這個表達式的結果并不是布爾值,而編譯器期望的是一個布爾值,而且不會從一個int數值中轉換得來。所以在編譯時,系統就會提示出現錯誤,有效地阻止我們進一步運行程序。所以這個缺點在Java里永遠不會造成更嚴重的后果。唯一不會得到編譯錯誤的時候是x和y都為布爾值。在這種情況下,x
= y屬于合法表達式。而在上述情況下,則可能是一個錯誤。<br>
在C和C++里,類似的一個問題是使用按位AND和OR,而不是邏輯AND和OR。按位AND和OR使用兩個字符之一(&或|),而邏輯AND和OR使用兩個相同的字符(&&或||)。就象“=”和“==”一樣,鍵入一個字符當然要比鍵入兩個簡單。在Java里,編譯器同樣可防止這一點,因為它不允許我們強行使用一種并不屬于的類型。<br>
<br>
3.1.13 造型運算符<br>
“造型”(Cast)的作用是“與一個模型匹配”。在適當的時候,Java會將一種數據類型自動轉換成另一種。例如,假設我們為浮點變量分配一個整數值,計算機會將int自動轉換成float。通過造型,我們可明確設置這種類型的轉換,或者在一般沒有可能進行的時候強迫它進行。<br>
為進行一次造型,要將括號中希望的數據類型(包括所有修改符)置于其他任何值的左側。下面是一個例子:<br>
<br>
void casts() {<br>
int i = 200;<br>
long l = (long)i;<br>
long l2 = (long)200;<br>
}<br>
<br>
正如您看到的那樣,既可對一個數值進行造型處理,亦可對一個變量進行造型處理。但在這兒展示的兩種情況下,造型均是多余的,因為編譯器在必要的時候會自動進行int值到long值的轉換。當然,仍然可以設置一個造型,提醒自己留意,也使程序更清楚。在其他情況下,造型只有在代碼編譯時才顯出重要性。<br>
在C和C++中,造型有時會讓人頭痛。在Java里,造型則是一種比較安全的操作。但是,若進行一種名為“縮小轉換”(Narrowing
Conversion)的操作(也就是說,腳本是能容納更多信息的數據類型,將其轉換成容量較小的類型),此時就可能面臨信息丟失的危險。此時,編譯器會強迫我們進行造型,就好象說:“這可能是一件危險的事情——如果您想讓我不顧一切地做,那么對不起,請明確造型。”而對于“放大轉換”(Widening
conversion),則不必進行明確造型,因為新類型肯定能容納原來類型的信息,不會造成任何信息的丟失。<br>
Java允許我們將任何主類型“造型”為其他任何一種主類型,但布爾值(bollean)要除外,后者根本不允許進行任何造型處理。“類”不允許進行造型。為了將一種類轉換成另一種,必須采用特殊的方法(字串是一種特殊的情況,本書后面會講到將對象造型到一個類型“家族”里;例如,“橡樹”可造型為“樹”;反之亦然。但對于其他外來類型,如“巖石”,則不能造型為“樹”)。<br>
<br>
1. 字面值<br>
最開始的時候,若在一個程序里插入“字面值”(Literal),編譯器通常能準確知道要生成什么樣的類型。但在有些時候,對于類型卻是曖昧不清的。若發生這種情況,必須對編譯器加以適當的“指導”。方法是用與字面值關聯的字符形式加入一些額外的信息。下面這段代碼向大家展示了這些字符。<br>
<br>
116-117頁程序<br>
<br>
十六進制(Base 16)——它適用于所有整數數據類型——用一個前置的0x或0X指示。并在后面跟隨采用大寫或小寫形式的0-9以及a-f。若試圖將一個變量初始化成超出自身能力的一個值(無論這個值的數值形式如何),編譯器就會向我們報告一條出錯消息。注意在上述代碼中,最大的十六進制值只會在char,byte以及short身上出現。若超出這一限制,編譯器會將值自動變成一個int,并告訴我們需要對這一次賦值進行“縮小造型”。這樣一來,我們就可清楚獲知自己已超載了邊界。<br>
八進制(Base 8)是用數字中的一個前置0以及0-7的數位指示的。在C,C++或者Java中,對二進制數字沒有相應的“字面”表示方法。<br>
字面值后的尾隨字符標志著它的類型。若為大寫或小寫的L,代表long;大寫或小寫的F,代表float;大寫或小寫的D,則代表double。<br>
指數總是采用一種我們認為很不直觀的記號方法:1.39e-47f。在科學與工程學領域,“e”代表自然對數的基數,約等于2.718(Java一種更精確的double值采用Math.E的形式)。它在象“1.39×e的-47次方”這樣的指數表達式中使用,意味著“1.39×2.718的-47次方”。然而,自FORTRAN語言發明后,人們自然而然地覺得e代表“10多少次冪”。這種做法顯得頗為古怪,因為FORTRAN最初面向的是科學與工程設計領域。理所當然,它的設計者應對這樣的混淆概念持謹慎態度(注釋①)。但不管怎樣,這種特別的表達方法在C,C++以及現在的Java中頑固地保留下來了。所以倘若您習慣將e作為自然對數的基數使用,那么在Java中看到象“1.39e-47f”這樣的表達式時,請轉換您的思維,從程序設計的角度思考它;它真正的含義是“1.39×10的-47次方”。<br>
<br>
①:John Kirkham這樣寫道:“我最早于1962年在一部IBM 1620機器上使用FORTRAN
II。那時——包括60年代以及70年代的早期,FORTRAN一直都是使用大寫字母。之所以會出現這一情況,可能是由于早期的輸入設備大多是老式電傳打字機,使用5位Baudot碼,那種碼并不具備小寫能力。乘冪表達式中的‘E’也肯定是大寫的,所以不會與自然對數的基數‘e’發生沖突,后者必然是小寫的。‘E’這個字母的含義其實很簡單,就是‘Exponential’的意思,即‘指數’或‘冪數’,代表計算系統的基數——一般都是10。當時,八進制也在程序員中廣泛使用。盡管我自己未看到它的使用,但假若我在乘冪表達式中看到一個八進制數字,就會把它認作Base
8。我記得第一次看到用小寫‘e’表示指數是在70年代末期。我當時也覺得它極易產生混淆。所以說,這個問題完全是自己‘潛入’FORTRAN里去的,并非一開始就有。如果你真的想使用自然對數的基數,實際有現成的函數可供利用,但它們都是大寫的。”<br>
<br>
注意如果編譯器能夠正確地識別類型,就不必使用尾隨字符。對于下述語句:<br>
long n3 = 200;<br>
它并不存在含混不清的地方,所以200后面的一個L大可省去。然而,對于下述語句:<br>
float f4 = 1e-47f; //10的冪數<br>
編譯器通常會將指數作為雙精度數(double)處理,所以假如沒有這個尾隨的f,就會收到一條出錯提示,告訴我們須用一個“造型”將double轉換成float。<br>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -