這一節(jié)會更深入地探討表達式。回憶一下,表達式是一段用來表示或計算值的代碼。表達式可以是文字、變量、函數(shù)調(diào)用或者這些的組合,由 +、> 這樣的運算符連接到一起。表達式的值可以賦給變量,在子程序調(diào)用中用作參數(shù),或者與其它的值組合成為更復(fù)雜的表達式。(這些值在某些情況下甚至可以被忽略掉,如果你想要這么干;這比你想象中更加常見)。表達式是編程的基礎(chǔ)。到目前為止,本書都只是順帶提到了表達式。這一節(jié)會告訴你一個完整的故事(這里會忽略一些不常見的運算符)。 表達式的基礎(chǔ)由文字(比如674、3.14、true 和 ‘X’)、變量和函數(shù)調(diào)用組成。還記得函數(shù)是一個帶返回值的子程序。你可能已經(jīng)看到過一些函數(shù)的例子,比如 TextIO 類的輸入程序以及 Math 類的數(shù)學(xué)計算函數(shù)。 Math 類還包含了一組數(shù)學(xué)常量,在數(shù)學(xué)表達式中非常有用:Math.PI 表示 π (圓周率)、 Math.E 表示 e(自然對數(shù)的基數(shù))。這些“常量”實際上都是 Math 類中 double 類型的成員變量。它們是數(shù)學(xué)常量的近似值,實際的精確值要求無限長度的數(shù)字。標準的 Integer 類包含了一組與 int 數(shù)據(jù)類型相關(guān)的常量:Integer.MAX_VALUE 是最大的 int,2147483647;Integer.MIN_VALUE 是最小的 int,-2147483648。類似地,Double 類包含了一些與 double 類型相關(guān)的常量。Double.MAX_VALUE 是最大的 double 值,而 Double.MIN_VALUE 是最小的正值。Double 還包含了表示無限的數(shù)值,Double.POSITIVE_INFINITY 和 Double.NEGATIVE_INFINITY。而特殊的 Double.NaN 表示未定義值。例如,Math.sqrt(-1) 的結(jié)果就是 Double.NaN。 文字、變量和函數(shù)調(diào)用都是簡單表達式。通過運算符可以將簡單表達式組合成復(fù)雜表達式。運算符包括 + 將兩個數(shù)值相加,> 比較兩個值大小,等等。當(dāng)表達式中包含了若干運算符時,就會出現(xiàn)優(yōu)先順序問題,它決定了運算符在計算式如何分組。例如,在表達式 “A + B * C” 中,B*C 會先計算,然后結(jié)果再與 A 相加。我們說,乘法 (*) 的優(yōu)先級比加法 (+) 高。如果默認的優(yōu)先級順序不是你想要的,那么可以使用括號明確指定你期望的分組。例如,你可以使用”(A + B) * C” 表明希望先將 A 與 B 相加再乘以 C。 這一節(jié)的后面會對Java中的運算符細節(jié)進行詳細地解釋。Java提供了很多運算符,我不會每個都進行介紹,但是這里會給出大多數(shù)最重要的運算符說明。 2.5.1 算數(shù)運算符算數(shù)運算符包括加法、減法、乘法和除法。它們的符號分別是 +、-、* 和 /。這些操作可以用于任意類型的數(shù)值:byte、short、int、long、float或double。(它們還可以用在 char 類型上,在這種情況下 char 被當(dāng)做 integer 使用;char 會被轉(zhuǎn)換成它的 Unicode 代碼,并代入算數(shù)運算符操作。)當(dāng)計算機實際計算時,計算中的兩個值必須是相同類型。如果你的程序告訴計算機輸入了兩個不同類型的值,那么計算機會將其中一個轉(zhuǎn)換成另一個的類型。例如,計算 37.4 + 10 這個表達式時,計算機會將整數(shù) 10 轉(zhuǎn)換成實數(shù) 10.0,然后計算 37.4 + 10.0。這被稱為類型轉(zhuǎn)換。通常,你不必關(guān)心表達式中的類型轉(zhuǎn)換,因為計算機會替你自動完成。 兩個數(shù)值(如果必要,會對其中一個進行類型轉(zhuǎn)換)計算后的結(jié)果與其類型一致。如果兩個 int 相乘,結(jié)果是 int;兩個 double 相乘,結(jié)果是 double,這是可預(yù)見的結(jié)果。但是,當(dāng)你使用 / 時必須非常小心。如果兩個 int 相除,結(jié)果是 int;如果商是分數(shù),會被舍去。例如,7/2 結(jié)果是 3,而不是 3.5。假設(shè)N是整型變量,那么 N/100 結(jié)果不是整數(shù),并且當(dāng) N 大于 1 時 1/N 等于 0!這是很多常見編程錯誤的根源。可以把其中一個運算符改為實數(shù),強迫計算機輸出實數(shù):比如,當(dāng)計算機處理 1.0/N 時,首先把N轉(zhuǎn)成實數(shù),從而與 1.0 的類型匹配,這樣得到的結(jié)果就是實數(shù)。 Java還提供了計算除法操作的余數(shù)。計算余數(shù)的運算符為 %。如果 A 和 B 都是整數(shù),那么 A % B 表示 A 除以 B 的余數(shù)。(然而,對于負數(shù)Java中的 % 與數(shù)學(xué)計算中的“取?!辈僮鞑煌T贘ava中,如果 A 或 B 為負數(shù),那么 A % B 的結(jié)果也是負數(shù))。例如,7 % 2 等于 1,34577 % 100 等于 77,50 % 8 等于 2。% 常被用來測試給定整數(shù)是奇數(shù)還是偶數(shù):如果 N % 2 等于0,那么N是偶數(shù);如果 N % 2 等于 1,那么N是奇數(shù)。一般來說,你可以通過 N % M 結(jié)果是否為 0,判斷整數(shù)N是否可以被M取模。 % 運算符也適用于實數(shù)。通常,A % B 表示從 A 中移除多個 B 后遺留的數(shù)值。例如,7.52 % 0.5 等于 0.02。 最后,你還可能需要一元減法運算符,得到一個數(shù)的負數(shù)。例如,-X 等價于 (-1)*X。出于完備性考慮,Java還提供了一元加法運算符,例如 +X。景觀在實際中沒有任何作用。 順便說一下,+ 操作還可以用來向 String 字符串連接任意類型的值。當(dāng)你使用 + 連接字符串時,這是另一種形式的類型轉(zhuǎn)換,任意的對象都會自動轉(zhuǎn)換為 String。 2.5.2 增加和減少你會發(fā)現(xiàn),為變量加 1 是編程中極其常見的操作,為變量減 1 也一樣。可以像下面這樣賦值為變量加 1: counter = counter + 1;goalsScored = goalsScored + 1; x = x + 1 語句的結(jié)果是,用變量 x 原來的值加 1 后再賦值給變量 x。也可以用 x++ 得到相同的效果(或者你可能會喜歡寫成 ++x)。實際上,這么寫會改變 x 的值,得到的效果與 “x = x + 1″ 一樣。上面的兩個語句可以改為: counter++;goalsScored++; 類似地,你也可以寫 x–(或 –x)從 x 中減1。也就是說,x– 與 x = x - 1 執(zhí)行了相同的計算。向變量加 1 稱為變量遞增,從變量減 1 稱為變量遞減。運算符 ++ 和 — 分別被稱為遞增運算符和遞減運算符。這些運算符可以用于任何數(shù)值類型的變量,以及char類型的變量( ‘A’++ 結(jié)果是 ‘B’)。 通常,運算符 ++ 和 — 用在語句中,比如 “x++;” 或 “x–;”。這些語句是改變 x 值的指令。然而,將x++、++x、x–或–x作為表達式或表達式的一部分也是合法的。也就是說,你可寫出下面的代碼: y = x++;y = ++x;TextIO.putln(--x);z = (++x) * (y--); “y = x++;”的效果是 x 變量加1,然后把某個值賦給 y。賦給 y 的值是表達式 x 加 1 之前的值。因此,假設(shè) x 等于 6,那么 “y = x++;” 執(zhí)行后,會將 x 變?yōu)?7,但是 y 的值被賦為 6。因為賦給 y 的是 x 加 1 前的舊值。而另一種寫法,++x 會得到加1后的新值。所以,還是假設(shè) x 等于 6, ”y = ++x;” 會把 x 和 y 都變?yōu)?7。運算符 — 也是類似的用法。 特別要注意,x = x++; 這個語句沒有改變 x 的值!這是因為賦給 x 的是 x 的舊值,即在語句執(zhí)行前 x 的值。最終結(jié)果是,x 增加了 1,但是馬上被改回了原來的值!你還需要記住,x++ 不等同 于 x + 1。表達式 x++ 改變了 x 的值,而 x + 1 沒有改變。 這里會讓你感到困惑,我從學(xué)生程序中看到很多由此造成的bug。我的建議是:不要寫這種讓人困惑的代碼。++ 和 — 只在單獨的語句使用,不要用成表達式。在接下來的示例中,我會遵循這條建議。 2.5.3 關(guān)系運算符Java提供了布爾比例和布爾表達式表示條件,條件結(jié)果可以為 true 或 false。組織布爾表達式可以通過關(guān)系運算符比較兩個值。 A == B Is A 'equal to' B?A != B Is A 'not equal to' B?A < b="" is="" a="" 'less="" than'="" b?a=""> B Is A 'greater than' B?A <= b="" is="" a="" 'less="" than="" or="" equal="" to'="" b?a="">= B Is A 'greater than or equal to' B? A == B A“等于”B?A != B A“不等于”B?A < b="" a“小于”b?a=""> B A“大于”B?A <= b="" a“小于等于”b?a="">= B A“大于等于”B? 這些運算符可以比較任意數(shù)值類型的數(shù)值。還可以用來比較char類型的值。對字符串來說,< 和="">被定義為根據(jù)字符的 Unicode值 進行比較(結(jié)果并不是完全按照字母順序比較,這可能不是你想要的。所有大寫字母小于小寫字母。) 使用布爾表達式時你應(yīng)當(dāng)記住,對計算機而言,布爾值并沒有什么特殊的地方。在下一章中,你會看到如何在循環(huán)和分支中使用它們。你可以像賦值給數(shù)字變量一樣,給布爾變量賦布爾值。函數(shù)會返回布爾值。 順帶說一下,運算符 == 和 != 也可以用來比較布爾值。在某些情況下這是非常有用的。例如,你可以看下面這段代碼: boolean sameSign;sameSign = ((x > 0) == (y > 0)); 關(guān)系運算符 <、>、<= 和="">= 不能比較String值。你可以合法地使用 == 和 != 來比較字符串,但是由于對象行為的差別,可能不會得到你期望的結(jié)果。(== 運算符可以檢查兩個對象的內(nèi)存地址是否相同,而不是判斷對象中值是否相等。對某些對象,在某種情況下,你可能想要做類似的檢查——但字符串不行。下一章我會再討論這個話題。)相反地,你要使用 equals()、equalsIgnoreCase() 和 compareTo(),這些在2.3.3章節(jié)中進行了討論,如何比較兩個字符串。 另一個 == 和 != 不起作用的地方是與 Double.NaN 比較。這個常量表示 double 類型的未定義值。無論 x 的值是否為 Double.NaN,x == Double.NaN 和 x != Double.NaN 都會返回 false!要檢測實數(shù)類型的值 x 是否為 Double.NaN,可以使用函數(shù) Double.isNaN(x) 返回判斷結(jié)果。 2.5.4 布爾運算符在英語中,復(fù)雜條件通過 and、or 和 not 組合在一起。例如,“If there is a test and you did not study for it…”,and、or 和 not 都是布爾運算符,在Java中也同樣存在。 在Java中,布爾運算符“and”表示為 &&。&& 運算符用來結(jié)合 2 個布爾值。結(jié)果還是 1 個布爾值。如果兩個值都是 true,那么結(jié)果為 true;如果其中一個為 false,那么結(jié)果為 false。例如,如果 x 等于 0 并且 y 等于 0,“(x == 0) && (y == 0)” 結(jié)果為true。 布爾運算符“or”在Java中表示為 ||。(由兩個垂直的行字符 | 組成。)如果 A 或 B 其中一個為 true 或者都為 true,那么表達式 “A || B” 結(jié)果為 true。只有 A 和 B 同時為 false 時,“A || B” 結(jié)果為 false。 運算符 && 和 || 被稱為短路版本的布爾運算符。也就是說,&& 或 || 的第二個操作符不一定會計算。考慮下面這個測試 (x != 0) && (y/x > 1) 假設(shè) x 的值實際為 0,在這種情況下,y/x 是未定義的觸發(fā)運算(除0)。然而,計算機永遠不會執(zhí)行這個觸發(fā),因為當(dāng)計算機對 (x != 0) 計算結(jié)果時,發(fā)現(xiàn)結(jié)果為 false。這時計算機知道 ((x != 0) && 任意表達式) 一定會為 false。因此,它不會再計算對第二個運算符求值。運算被短路,從而避免了除0的情況。(這個聽起來有點偏技術(shù)性,事實也是如此。但有時候,會讓編程生活更輕松些。) 布爾運算符“not”是一元運算符。在Java中用 ! 表示,寫在單個運算對象的前面。例如,假設(shè) test 是一個布爾變量,那么 test = ! test; 將會對 test 的值取反,從 true 變?yōu)?false 或者從 false 變?yōu)?true。 2.5.5 條件運算符任何優(yōu)秀的編程語言都有一些漂亮的小功能。雖然不是必須的功能,但可以讓你在使用時感覺很酷。Java也有,條件運算符中的三元運算符。它有 3 個操作數(shù),有 2 個部分:,? 和 : 組合在一起。三元運算符形式如下: boolean-expression ? expression1 : expression2 計算機會檢測布爾表達式的值。如果值為 true,會計算 expression1,否則計算 expression2。例如: next = (N % 2 == 0) ? (N/2) : (3*N+1); 如果N是偶數(shù),會把 N/2 賦給 next(即 N % 2 == 0 為 true);如果N是基數(shù),會把 (3*N+1) 賦給 next(這里的括號不是必須的,但是會讓表達式更容易理解)。 2.5.6 賦值運算符和類型轉(zhuǎn)換你可能已經(jīng)對賦值表達式非常熟悉,使用 “=” 將表達式賦值給變量。實際上,在某種意義上 = 也是運算符,可以將它用作表達式或者作為復(fù)雜表達式一部分。表達式 A=B 與向 A 賦值的語句作用相同。因此,如果你想要把 B 的值賦給 A,同時判斷值是否為 0,可以這么寫: if ( (A=B) == 0 )... 通常,我會強調(diào)不要那么做! 通常,表達式中右邊的類型必須和左邊一致。然而,在某些情況下,計算機會對表達式的值自動轉(zhuǎn)換,以匹配變量的類型。比如在數(shù)值類型 byte、short、int、ong、float、double 中,列表中靠前的類型數(shù)值可以自動轉(zhuǎn)換為列表中靠后的類型。 int A;double X;short B;A = 17;X = A; // OK; A is converted to a double //OK;A被自動轉(zhuǎn)換為double類型B = A; // illegal; no automatic conversion //非法;不能從int自動轉(zhuǎn)換為short // from int to short 在不影響語義的情況下,轉(zhuǎn)換應(yīng)當(dāng)自動進行。任何int應(yīng)當(dāng)可以被轉(zhuǎn)換為數(shù)值相同的 double 類型。然而,int 值中有一些超過了short 類型的合法范圍。比如不能將 100000 轉(zhuǎn)為 short,因為 short 的最大值是 32767. 在某些情況下,比如在不能自動轉(zhuǎn)換的情況下你可能想要進行強制轉(zhuǎn)換。這里,你需要使用類型轉(zhuǎn)換。類型轉(zhuǎn)換可以把類型名放在括號里,放在你需要轉(zhuǎn)換的數(shù)值前面。例如: int A;short B;A = 17;B = (short)A; // OK; A is explicitly type cast // OK;A可以顯示地把數(shù)值轉(zhuǎn)換為short類型 // to a value of type short 你可以將任何數(shù)值類型轉(zhuǎn)換為其他數(shù)值類型。然而,你應(yīng)當(dāng)注意,轉(zhuǎn)換過程中可能會改變數(shù)值。例如,(short)100000 等于 -31072。(-31072 是通過 100000 丟掉2個字節(jié),保留 4 個字節(jié)得到的 short 值——轉(zhuǎn)換中會丟失 2 個字節(jié)的信息。) 當(dāng)你將實數(shù)轉(zhuǎn)為整型時,小數(shù)部分被丟掉了。例如,(int)7.9453 等于 7。另一個類型轉(zhuǎn)換的例子,從 1 到 6 范圍中得到隨機整數(shù)。函數(shù) Math.random() 會返回 0.0 到 0.9999… 之間的實數(shù),因此 6*Math.random() 的結(jié)果在 0.0 和 5.999… 之間。類型轉(zhuǎn)換操作符 (int) 可以用來將結(jié)果轉(zhuǎn)為整形:(int)(6*Math.random())。因此,(int)(6*Math.random()) 結(jié)果會得到 0、1、2、3、4、5 之間的某個整數(shù)。要得到 1 到 6 之間的隨機數(shù),可以加 1:”(int)(6*Math.random()) + 1″。(6*Math.random() 周圍的括號是必須的,因為用括號來保證運算的優(yōu)先順序;如果沒有括號,類型轉(zhuǎn)換運算符只能對 6 起效)。 char 類型與整型幾乎等價。你可以將 char 類型賦值給任意整型變量。你還可以將 0 到 65535 內(nèi)的常量賦值給 char 變量。你還可以顯示地在 char 和數(shù)值型之間進行類型轉(zhuǎn)換,比如 (char)97 得到 ‘a(chǎn)’,(int)’+’ 是 43,(char)(‘A’ + 2) 等于 ‘C’。 String 和其它類型之間的不能進行類型轉(zhuǎn)換。任意類型轉(zhuǎn)換成字符串的一種方法,可以將他們與一個空字符串連接。例如,”' + 42 可以得到字符串 “42″。但是還有更好的辦法,使用 String 類中的靜態(tài)方法 String.valueOf(x)。String.valueOf(x) 返回輸入 x 轉(zhuǎn)換得到的 String。例如,String.valueOf(42) 返回字符串 “42″。而且,如果 ch 是一個 char 變量,那么 String.valueOf(ch) 返回長度為 1 的字符串,字符串中內(nèi)容是 ch 變量中的唯一一個字符。 也可以將特定字符串轉(zhuǎn)換為其它類型的值。例如,字符串”10″ 可以被轉(zhuǎn)轉(zhuǎn)換為整型值 10,而字符串 “17.42e-2″ 可以轉(zhuǎn)換為 double 值 0.1742。在Java中,這些轉(zhuǎn)換可以由內(nèi)建方法完成。 標準的 Integer類 提供了靜態(tài)成員函數(shù),可以將 String 轉(zhuǎn)為 int。特別地,如果 str 是任意的 String 表達式,Integer.parseInt(str) 會試著將str的值轉(zhuǎn)為int類型。例如 Integer.parseInt(“10″) 得到 int 值 10。如果 Integer.parseInt 傳入的參數(shù)是非法的 int 值,那么會返回錯誤。 類似地,標準的 Double 類提供了 Double.parseDouble方法。如果 str 是 String 類型,調(diào)用 Double.parseDouble(str) 方法會試圖將 str 轉(zhuǎn)為 double 類型的值。當(dāng) str 表示的是非法 double 值,就會返回錯誤。 讓我們會到賦值語句。Java有許多賦值運算符變種,用來保存類型。例如,”A += B” 等價于 “A = A + B”。除了關(guān)系運算符,每個Java運算符都可以處理 2 個操作數(shù),這樣就得到了 1 個類似的賦值運算符。例如: x -= y; // same as: x = x - y; 等價于:x = x - y;x *= y; // same as: x = x * y; 等價于:x = x * y;x /= y; // same as: x = x / y; 等價于:x = x / y;x %= y; // same as: x = x % y; 等價于:x = x % y;q &&= p; // same as: q = q && p; (for booleans q and p) 等價于:q = q && p; (q和p都是布爾型) 組合式賦值運算符 += 甚至可以用于String?;貞浺幌?,+ 運算符可以將 string 作為其中的一個操作數(shù),表示連接操作。既然 str += x 等價于 str = str + x,那么當(dāng) += 將 string 作為操作數(shù)時,這個操作就把右邊的操作數(shù)附到字符串的結(jié)尾。例如,如果 str 的值是 “tire”,那么語句 str += ’d'; 就會返回 str 值為 “tired”。 2.5.7 優(yōu)先規(guī)則如果在表達式中使用了多個運算符,并且沒有使用括號來顯示地指定計算順序,那么你就需要考慮優(yōu)先規(guī)則來確定實際的計算順序。(建議:不要讓你自己和程序的閱讀者產(chǎn)生困惑,大方地使用括號吧。) 下面是本章討論過的運算符列表,按照優(yōu)先級從高(第一個計算)到低(最后計算)順序排列: Unary operators: 一元運算符: ++, --, !, unary -, unary +, type-castMultiplication and division: 乘法和除法: *, /, %Addition and subtraction: 加法和減法: +, -Relational operators: 關(guān)系運算符: <,>, <=,>=Equality and inequality: 相等和不等: ==, !=Boolean and: 布爾與: &&Boolean or: 布爾或: ||Conditional operator: 條件運算符: ?:Assignment operators: 賦值運算符: =, +=, -=, *=, /=, %= 同一行的運算符優(yōu)先級相等。在沒有括號的情況下,優(yōu)先級相同的運算符串在一起,一元運算符和賦值運算符的計算順序是從右到左,而剩下的其它運算符計算順序是從左到右。例如 A*B/C 表示 (A*B)/C,而 A=B=C 表示 A=(B=C)。(考慮到 B=C 作為表達式的同時也進行了給 B 賦值的運算,你能看出A=B=C表達式是如何計算的嗎?) |
|
來自: yy99k > 《java編程入門》