Mais conteúdo relacionado Semelhante a SCJP ch04 (11) SCJP ch041. 第 4 章
運算式 (Expression)
本投影片(下稱教用資源)僅授權給採用教用資源相關之旗標書籍為教科書之授課老師(下稱老師)專用,老
師為教學使用之目的,得摘錄、編輯、重製教用資源(但使用量不得超過各該教用資源內容之 80% )以製作為
輔助教學之教學投影片,並於授課時搭配旗標書籍公開播放,但不得為網際網路公開傳輸之遠距教學、網路教
學等之使用;除此之外,老師不得再授權予任何第三人使用,並不得將依此授權所製作之教學投影片之相關著
作物移作他用。
著作權所有 © 旗標出版股份有限公司
3. 前言
在上一章中 , 我們已經看過了 Java 程式語言中的各
種資料型別 , 有了這些資料後 , 才能夠進行處理。
其實在 Java 程式中 , 大部分的處理工作就是運算 ,
像是大家都很熟悉的四則運算、或是邏輯比較 , 以及
低階的位元運算等等。有了這些運算的功能 , 才能夠
將輸入的資料轉換成所需的結果。
因此 , 在這一章中 , 就要將重點放在 Java 程式語言
所提供的各項運算功能。
4. 4-1 甚麼是運算式?
在 Java 程式語言中 , 大部分的敘述都是由運算式
(Expression) 所構成。所謂的運算式 , 則是由一組
一組的運算子 (Operator ) 與運算元 (Operand) 所
構成。
其中 , 運算子代表的是運算的種類 , 而運算元則是要
運算的資料。舉例來說:
就是一個運算式 , 其中 + 就是運算子 , 代表要進行
加法運算 , 而要相加的則是 5 與 3 這兩個資料 , 所
以 5 與 3 就是運算元。
5. 甚麼是運算式?
要注意的是 , 不同的運算子所需的運算元數量不同 ,
像是剛剛所提的加法 , 就需要二個運算元 , 這種運算
子稱為二元運算子 (Binary Operator) ;如果運算子
只需單一個運算元 , 就稱為單元運算子 (Unary
Operator) 。
另外 , 運算元除了可以是字面常數以外 , 也可以是變
數 , 例如:
7. 甚麼是運算式?
每一個運算式都有一個運算結果 , 以加法運算來說 ,
兩個運算元相加的結果就是加法運算式的運算結果。
當某個運算元為一個運算式時 , 該運算元的值就是這
個運算式的運算結果。以剛剛的例子來說 , 3 * 4 的
結果 12 就是 3 * 4 這個運算式的運算結果 , 它就
會作為前面加法運算的第二個運算元的值 , 相當於將
原本的運算式改寫為 5 + 12 了。
另外 , 在運算式當中 , 也可以如同數學課程中所學的
一樣 , 任意使用配對的小括號 "( )", 明確表示計算的
方式 , 舉例來說。
9. 甚麼是運算式?
其中第 4 與第 5 行的運算式如果將括號去除 , 那麼
兩個運算式完全一樣 , 可是因為加上了括號 , 所以兩
個運算式的順序並不相同 , 最後的結果也不一樣。
有了以上的基本認識後 , 就可以進一步瞭解各種運算
了。以下就分門別類 , 介紹 Java 程式語言中的運算
子。
10. 運算子的語法
在以下的章節中 , 我們會在說明每一個運算子之前 ,
列出該運算子的語法 , 舉例來說 , 指定運算子的語法
就是:
這個意思就表示要使用指定運算子 (=) 的話 , 必須
有 2 個運算元 , 左邊的運算元一定要是一個變數
( 以 var 表示 ) , 右邊的運算元則沒有限制。
12. 4-2 指定運算子 (Assignment Operator)
指定運算子是用來設定變數的內容 , 它需要 2 個運
算元 , 左邊的運算元必須是一個變數 , 而右邊的運算
元可以是變數、常數或是運算式。
這個運算子的作用 , 就是將右邊運算元的值或運算結
果 , 儲存到左邊的變數中。
請看以下的範例。
14. 指定運算子 (Assignment Operator)
其中 , 第 3 行是直接使用字面常數設定變數的值。
第 4 行則是使用指定運算子將右邊運算式的運算結
果放入左邊的變數 i 中。
而第 5 行就是使用指定運算子將右邊變數 i 的內容
放到左邊的變數 j 中 , 因此 , 最後的結果就使得 i
與 j 這兩個變數的內容一模一樣了。
15. 4-2-1 當成運算元的指定運算式
前面提過 , 每一個運算式都有一個運算結果 , 而指定
運算式的運算結果就是放入指定運算子左邊變數的內
容。
因此 , 我們可以運用之前所說:『運算式也可以是某
個運算式的運算元』 , 將指定運算式作為運算元使
用 , 舉例來說。
17. 當成運算元的指定運算式
在第 4 行中 , 就使用了 j = 3 這個指定運算式當作
加法的其中一個運算元 , 因此 , 這一行的執行過程
就像是這樣:
1. 先將 3 放到變數 j 中 , 所以 j 的內容變成 3, 而 j
= 3 這個運算式的運算結果也是 3 。
2. 將 j = 3 這個運算式的結果 ( 也就是 3) 與 5 相加 ,
得到 8 。
3. 將 (j = 3) + 5 這個運算式的結果 ( 也就是 8) 放入變
數 i 中 , 所以 i 的內容就變成 8 了。
19. 同時指定給多個變數
第 4 行的指定運算會將 3 + 5 這個運算式的運算結
果 (8) 放入變數 l 中 , 而 l = 3 + 5 這個運算式的運
算結果 ( 一樣是 8) 放入 k 中 , 因此 l 與 k 的內
容就都是 8 。
依此類推 , 最後 i 、 j 、 k 、 l 這 4 個變數的內容
就全部都是 8 了。
20. 4-3 數值 運算
在所有的運算子中 , 大家最熟悉的可能就要屬數值運
算了 , 撰寫程式時使用率也極高。
因此 , 在這一小節中 , 所要介紹的是可以運用在數值
型別資料的各種運算子。
23. 四則運算
要特別注意的是 , 由於 i 與 j 都是 int 型別 , 因此
在進行除法時 , 計算的結果也會是整數 , 而不會出現
小數 , 因此當無法整除時 , 所得到的就只是整數的商
。
▪ 瞭解這一點 , 就可以知道在 Java 程式中 , (5 / 3) * 2
與 (5 * 2) / 3 的結果是不同的。
26. 4-3-2 遞增 與遞減運算
由於在設計程式的時候 , 經常會需要將變數的內容遞
增或是遞減 , 因此 Java 也設計了簡單的運算子 , 可
以用來代替使用加法運算子或減法運算子來幫變數加
1 或是減 1 的敘述。
如果您需要幫變數加 1, 可以使用 ++ 這個遞增 運算
子 (Increment Operator) ;如果需要幫變數減 1,
則可以使用 -- 這個遞減運算子 (Decrement
Operator) 。
28. 遞增 與遞減運算
在第 4 行使用了遞增運算子 , 因此變數 i 的內容會
變成 5 + 1, 也就是 6 。
而在第 6 行中 , 使用了遞減運算子 , 因此變數 i 就
又變回 5 了。
要注意的是 , 遞增或是遞減運算子可以寫在變數的後
面 , 也可以寫在變數的前面 , 但其所代表的意義並不
相同 , 請看這個範例。
30. 遞增 與遞減運算
我們分別在第 3 、 8 行將 i 的內容設定為 5, 然後
在第 4 與第 9 行的運算式中使用遞增運算子設定變
數 j 的內容。
這 2 行程式唯一的差別就是遞增運算子的位置一個
在變數後面、一個在變數前面 , 結果卻不相同。
主要的原因就是當遞增運算子放在變數後面時 , 雖然
會遞增變數的值 , 但遞增運算式的運算結果卻是變數
尚未遞增 前的原始值。
因此 , 第 4 行的運算式就相當於以下這行程式:
31. 遞增 與遞減運算
這種方式稱為後置遞增 運算子 (Post Increment
Operator) 。
如果把遞增運算子擺在變數之前 , 那麼遞增運算式的
運算結果就會是變數遞增後的內容。
因此 , 第 9 行的敘述就相當於以下這行程式:
32. 遞增 與遞減運算
由於遞增運算式的運算結果是變數遞增後的值 , 所以
++i 會讓 i 的值先變成 6 之後才和 5 相加 , 設定
給 j, 因此 j 就變成 11 了。
這種方式稱之為前置遞增 運算子 (Prefix Increment
Operator) 。
▪ 前置與後置的差異往往是考題的陷阱 , 請特別留意。
33. 遞增 與遞減運算
要特別提醒的是 , 遞增與遞減運算子只能用在變數上
, 也就是說 , 您不能撰寫這樣的程式:
另外 , 遞增或是遞減運算也可以使用在浮點數值 型別
的變數上 , 而非只能用在整數變數上。
36. 4-4 布林運算 (Logical Operation)
在這一小節中要介紹的是布林運算 , 也就是運算式的
運算結果是布林值的運算子 , 這類運算對於下一章流
程的控制以及用來表示某種狀態是否成立時特別有用
。
44. 邏輯運算子 (Logical Operator)
各個運算子的意義如下:
▪ & 與 && 運算子是邏輯且 (AND) 的意思 , 當兩個
運算元的值都是 true 的時候 , 運算結果就是 true,
否則就是 false 。
▪ | 與 | | 運算子是邏輯或 (OR) 的意思 , 兩個運算元
中只要有一個是 true, 運算結果就是 true, 只有在兩
個運算元的值都是 false 的情況下 , 運算結果才會是
false 。
▪ ^ 則是邏輯互斥 (XOR, eXclusive OR) 的運算 ,
當兩個運算元的值不同時 , 運算結果為 true, 否則為
46. 邏輯運算子 (Logical Operator)
第 4 、 5 行由於 b 是 false, 所以運算結果為
false 。第 6 、 7 行因為 a 是 true, 所以結果是
true 。
第 8 行因為 a 與 b 的值不同 , 所以互斥運算的結
果是 true 。
10 ~ 14 行則因為 a 和 c 都是 true, 所以除了互斥
運算以外 , 其餘的運算結果都是 true 。
47. 邏輯運算子 (Logical Operator)
您可能覺得奇怪 , & 、 | 這一組運算子和 && 、 | |
這一組運算子的作用好像一模一樣 , 為什麼要有兩組
功用相同的運算子呢?
其實這兩組運算子進行的運算雖然相同 , 但是
&& 、 | | 這一組運算子會在左邊的運算元就可以決
定運算結果的情況下忽略右邊運算元。
請看以下這個範例。
49. 邏輯運算子 (Logical Operator)
您可以發現 , 雖然第 5 與 10 行的運算結果都一樣
, 但是它們造成的效應卻不同。
在第 10 行中 , 由於 || 運算子左邊的運算元是 true,
因此不需要看右邊的運算元就可以知道運算結果為
true 。所以 i++ == 4 這個運算式根本就不會執行 , i
的值也就不會遞增 , 最後看到 i 的值原封不動。
但反觀第 5 行 , 由於是使用 | 運算子 , 所以會把兩
個運算元的值都求出 , 因此就會遞增變數 i 的內容
了。
依此類推 , & 運算子與 && 的運算子也是如此。
50. 邏輯運算子 (Logical Operator)
像這樣只靠左邊的運算元便可推算運算結果 , 而忽略
右邊運算元的方式 , 稱為短路模式 (Short Circuit ),
表示其取捷徑 , 而不會浪費時間繼續計算右邊運算元
的意思。
在使用這一類的運算子時 , 便必須考量到短路模式的
效應 , 以避免有些我們以為會執行的動作其實並沒有
執行的意外。
▪ 短路或不短路往往也是考題的陷阱 , 請多留意。
51. 4-5 位元運算 (Bitwise Operation)
在 Java 中 , 整數型別的資料是以 1 或多個位元組
透過 2 進位系統來表示 , 例如 , 以 byte 型別的資
料來說 , 就是用一個 byte 來表示數值 , 其中最高位
元是正負號 , 像是 2 拆解成 8 個位元就是:
52. 位元運算 (Bitwise Operation)
而負數是以 2 的補數法 (2's Complement), 也就是
其絕 對值 - 1 的補數 (Complement) 表示 , 亦即其
絕對值減 1 後以 2 進位表示 , 然後將每一個位元的
值反向 , 例如:
位元運算就是以位元為基本單位的運算。
53. 4-5-1 位元邏輯運算子
(Bitwise Logical Operator)
如果運算式的 2 個運算元都是整數 , 那麼 ^ 、 | 與
& 進行的並不是前面所介紹的布林值邏輯運算 , 而是
進行位元邏輯運算 , 也就是將 2 個運算元的對應位
元兩兩進行邏輯運算 , 得到運算式的結果。
請看範例。
55. 位元邏輯運算子 (Bitwise Logical Operator)
由於 2 的 2 進位表示法為 00000010, 而 -2 的 2
進位表示法為 11111110, 所以 2 | -2 就是針對對應
位元兩兩進行邏輯或的運算 , 對應位元中有一個值為
1 則結果即為 1, 否則就是 0 :
結果就是 11111110, 即 -2 。
56. 位元邏輯運算子 (Bitwise Logical Operator)
2 & -2 就是針對對應位元兩兩進行邏輯且的運算 , 只
有對應位元的值都是 1 時結果才為 1, 否則即為 0
:
結果就是 00000010, 亦即 2 。
57. 位元邏輯運算子 (Bitwise Logical Operator)
2 ^ -2 就是針對對應位元兩兩進行邏輯互斥的運算 ,
當對應位元的值不同時為 1, 否則為 0 :
結果就是 11111100, 亦即 -4 。
▪ 請特別留心負數的 2 進位表示法 , 否則計算結果會差
之千里。
61. 4-5-3 位元移位運算子 (Shift Operator)
位元移位運算子需要 2 個整數型別的運算元 , 運算
的結果就是將左邊的運算元以 2 進位表示後 , 依據
指定的方向移動右邊運算元所指定的位數。
67. 移位運算與乘除法
由於移位運算是以位元為單位 , 如果是向左移 1 位 ,
就等於是原本代表 1 的位數移往左成為代表 2 的位
數、而原本代表 2 的位數則往左移 1 位變成代表 4
的位數 , ..., 依此類推 , 最後就相當於把原數乘以
2 ; 如果移 2 位 , 就變成乘以 4 了。
相同的道理 , 當使用 >> 時 , 右移 1 位的運算就等
於是除以 2 、右移 2 位就變成除以 4 。
對於整數來說 , 使用位移運算因為不牽涉到數值的計
算 , 會比使用乘、除法來的有效率 , 可以善加利用。
70. 型別自動轉換
在使用移位運算時 , 請特別注意 , 除非左邊的運算元
是 long 型別 , 否則 Java 會先把左邊運算元的值轉
換成 int 型別 , 然後才進行移位的運算。這對於負數
的 >>> 運算會有很大的影響。
舉例來說 , 如果 Shift.java 的第 3 行將 i 、 j 宣告
為 byte, 並且以一個位元組來進行移位運算的話 , -2
>>> 1 的結果應該如下所示。
71. 型別自動轉換
不過實際上因為 -2 會先被轉換成 int 型別 , 因此移
位運算的結果和 Shift.java 一樣 , 還是
2147483647 。
有關 Java 在進行運算時 , 對於運算元進行的這類轉
換 , 會在 4-7 節說明。
72. 4-6 運算式的運算順序
到上一節為止 , 雖然已經瞭解了 Java 中大部分運算
子的功用 , 不過如果不小心 , 可能會寫出令您自己意
外的程式。舉例來說 , 以下這個運算式:
您能夠猜出來變數 i 最後的內容是甚麼嗎?
為了確認 i 的內容 , 必須先瞭解當一個運算式中有
多個運算子時 , Java 究竟是如何解譯這個運算式?
79. 4-6-2 運算子的結合性 (Associativity)
另外一個影響運算式計算結果的因素 , 稱為結合性。
所謂的結合性 , 是指對於優先順序相同的運算子 , 彼
此之間的計算順序。請先看以下這個運算式:
由於運算式中都是除法運算子 , 優先順序自然相同 ,
但是左邊的除法先算還是右邊的除法先算 , 結果顯然
不同。
81. 運算子的結合性 (Associativity)
如果以左邊的除法運算子為優先 , 就會解譯為這樣:
變數 i 的值就會是 4 / 2, 也就是 2 。但如果是以右
邊的除法運算子為優先 , 就會解譯成這樣:
變數 i 的值就變成 8 / 1, 成為 8 了。
和運算子間的優先順序一樣 , 顯然 Java 必須要有一
套規則 , 才能在撰寫程式時 , 確認運算結果的正確性
, 而不會有意外的結果。
82. 運算子的結合性 (Associativity)
以剛剛所舉的除法運算子來說 , Java 就規定了它的
結合性為左邊優先。
也就是說 , 當多個除法運算子串在一起時 , 會先從左
邊的除法運算子開始運算。
因此 , 在前面的例子中 , 會以左邊的除法運算子優先
, 計算出的結果再成為第 2 個除法運算子的運算元 ,
也就是採取第 1 種解譯的方法。來看看實際的程式
。
85. 運算子的結合性 (Associativity)
其中第 4 行就是依靠指定運算子右邊優先的結合性 ,
否則如果指定運算子是左邊優先結合的話 , 就變成:
如此將無法正常執行 , 因為第 2 個指定運算子左邊
需要變數作為運算元 , 但左邊這個運算式 i = j 的運
算結果並不是變數 , 而是數值。
91. 解譯運算式
從優先等級最高的運算子開始 , 找出它的運算元 , 然
後用括號將這個運算子所構成的運算式標示起來 , 視
為一個整體 , 以做為其他運算子的運算元。
如果遇到相鄰的運算元優先等級相同 , 就套用結合性
, 找出計算順序。
依此類推 , 一直標示到優先等級最低的運算子為止。
94. 解譯運算式
可想而知 , 如果每次看到這樣的運算式 , 都要耗費時
間才能確定其運算的順序 , 不但難以閱讀 , 而且撰寫
的時候也可能出錯。
因此 , 建議您最好使用括號明確的標示出運算式的意
圖 , 以便讓撰寫程式的您以及可能會閱讀程式的別人
都能夠一目了然 , 清清楚楚計算的順序。
像是剛剛所舉的例子來說 , 至少要改寫成這樣 , 才不
會對於計算的順序有所誤會:
96. 解譯運算式
其中第 4 行指定運算子右邊的運算元如果對於運算
子的歸屬解譯不同 , 結果就會不同。如果解譯為:
那麼運算結果就是 6, 而且 i 變為 4 、 j 的值不變。
但如果解譯成這樣:
那麼運算結果就會是 7, 而且 i 不變 , 但 j 會變成
4。
97. 解譯運算式
如果解譯成這樣:
那麼運算結果就是 6, 而且 i 、 j 的值均不變。
事實上 , Java 會由左往右 , 以最多字元能識別出的
運算子為準 , 因此真正的結果是第一種解譯方式。
為了避免混淆 , 一樣建議您在撰寫這樣的運算式時 ,
加上適當的括號來明確分隔運算子。
98. 4-7 資料的轉型 (Type Conversion)
到目前為止 , 已經把各種運算子的功用以及運算式的
運算順序都說明清楚 , 不過即便如此 , 您還是有可能
寫出令自己意外的運算式。
因為 Java 在計算運算式時 , 除了套用之前所提到的
結合性與優先順序以外 , 還使用了幾條處理資料型別
的規則 , 如果不瞭解這些 , 撰寫程式時就會遇到許多
奇怪不解的狀況。
101. 數值 運算的自動提升 (Promotion)
Java 編譯器居然說可能會漏失資料?
其實這並不是您的錯 , 而是您不曉得 Java 在計算運
算式時所進行的額外動作。
以下就針對不同的運算子 , 詳細說明 Java 內部的處
理方式。
103. 雙運算元的運算子
如果是二元運算子 , 規則如下:
1. 如果有任一個運算元是 double 型別 , 那麼就將另一
個運算元提升為 double 型別。
2. 否則 , 如果有任一個運算元是 float, 那麼就將另一
個運算元提升為 float 型別。
3. 否則 , 就再看是否有任一個運算元是 long 型別 , 如
果有 , 就將另外一個運算元提升為 long 型別。
4. 如果以上規則都不符合 , 就將 2 個運算元都提升為
int 型別。
簡單來說 , 就是將兩個運算元的型別提升到同一種
型別。
104. 數值 運算的自動提升 (Promotion)
根據這樣的規則 , 就可以知道剛剛的 Promotion.java
為什麼會有問題了。
由於 Java 會把 >> 運算子兩邊的運算元都提升為
int 型別 , 因此 i >> 1 的運算結果也是 int 型別 ,
但是 i 卻是 byte 型別 , 如果想把 int 型別的資料
放到 byte 型別的變數中 , Java 就會擔心數值過大 ,
無法符合 byte 型別可以容納的數值範圍。
這就好比如果您想把一個看起來體積比保管箱大的物
品放進保管箱中 , 服務人員自然不會准許。
105. 智慧的整數數值 設定
如果是使用字面常數設定型別為 char 、 byte 、或是
short 的變數 , 那麼即使 Java 預設會將整數的字面
常數視為 int 型別 , 但只要該常數的值落於該變數所
屬型別可表示的範圍內 , 就可以放入該變數中。
也就是說 , 以下這行程式是可以的:
但此項規則並不適用於 long 型別的字面常數 , 像是
以下這行程式:
107. 4-7-2 強制轉型 (Type Casting)
那麼到底要如何解決這個問題呢?
如果以保管箱的例子來說 , 除非可以動手把物品擠壓
成能夠放入保管箱的大小 , 否則怎麼樣都放不進去。
在 Java 中也是一樣 , 除非您可以自行保證要放進去
的數值符合 byte 的可接受範圍 , 否則就無法讓您將
int 型別的資料放入 byte 型別的變數中。
這個保證的方法就稱為強制轉型 (Cast), 請看以下的
程式。
109. 強制轉型 (Type Casting)
這個程式和剛剛的 Promoting.java 幾乎一模一樣 ,
差別只在於將第 4 行中原本的移位運算整個括起來 ,
並且使用 (byte) 這個轉型運算子 (Casting
Operator) 將運算結果轉成 byte 型別。
這等於是告訴 Java 說 , 我要求把運算結果變成
byte 型別 , 後果我自行負責。
透過這樣的方式 , 您就可以將運算結果放回 byte 型
別的變數中了。
111. 強制轉型的風險
在第 6 行中 , 因為 i 的值為 3, 符合 byte 型別的
範圍 , 轉型後並不會有問題。
但是第 11 行中 , 因為 i 的值為 199, 已經超過
byte 的範圍 , 由 int 強制轉型為 byte 時只會留下
最低的 8 個位元 , 使得轉型後 b 的值變成
11000111, 反而變成 -57 了。
113. 字面常數的型別
除非特別以字尾字元標示 , 否則 Java 會將整數的字
面常數視為是 int 型別 , 而將帶有小數點的字面常
數視為是 double 型別。
撰寫程式時 , 常常會忽略這一點 , 導致得到意外的運
算結果 , 甚至於無法正確編譯程式。
114. 指定運算子的轉型
在使用指定運算子時 , 會依據下列規則將右邊的運算
元自動轉型:
▪ 1. 如果左邊運算元型別比右邊運算元型別的數值範圍
要廣 , 就直接將右邊運算元轉型成左邊運算元的型別
。
▪ 2. 如果左邊的運算元是 byte 、 short 、或是 char
型別的變數 , 而右邊的運算元是僅由 byte 、 short 、
int 、或是 char 型別的字面常數所構成的運算式 , 並
且運算結果落於左邊變數型別的數值範圍內 , 那麼就
會將右邊運算元自動轉型為左邊運算元的型別。
116. 指定運算子的轉型
像是第 3 行指定運算子的右邊就是 int 型別 , 但左
邊是 byte 型別的變數 , 可是因為右邊的運算式僅由
字面常數構成 , 且計算結果符合 byte 的範圍 , 一樣
可以放進去。
第 4 行則很簡單 , 右邊 byte 型別的資料可以直接
放入左邊 int 型別的變數中。如果您把第 3 行改成
這樣:
那麼由於右邊運算式的運算結果超出了 byte 的範圍
, 連編譯的動作都無法通過。
117. 轉型的種類
在 Java 中 , 由 byte 到 int 這種由數值範圍較小的
基本型別轉換為範圍較大的基本型別 , 稱之為寬 化轉
型 (Widening Primitive Conversion) 。
反過來的方向 , 則稱之為窄化轉型 (Narrowing
primitive Conversion) 。
121. 複合指定運算子
(Compound Assignment Operator)
其中第 6 行的程式如果不使用複合指定運算子 , 並
不能單純的改成這樣:
因為 Java 會將 4.6 當成 double 型別 , 而依據上
一節的說明 , 為了讓 i 可以和 4.6 相加 , i 的值會先
被轉換成 double 型別。
因此 i + 4.6 的結果也會是 double 型別 , 但 i 卻是
int 型別 , 因此指定運算在編譯時會發生錯誤。正確
的寫法應該是:
122. 複合指定運算子
(Compound Assignment Operator)
而這正是複合指定運算子會幫您處理的細節 , 它會將
左邊運算元的值取出 , 和右邊運算元運算之後 , 強制
將運算結果轉回左邊運算元的型別 , 然後再放回左邊
的運算元。這樣一來 , 您就不需要自己撰寫轉換型別
的動作了。
▪ 一般人很容易忽略這個細微的差異 , 導致計算出錯誤
的結果。
除了 += 以外 , *= 、 / = 、 %= 、 = 、 <<= 、 >>= 、 >>>= 、 &= 、 ^= 、以及 | = 也
是可用的複合指定運算子。
123. 4-8-2 條件運算子 (Conditional Operator)
條件運算子是比較特別的運算子 , 它總共需要 3 個
運算元 , 分別以 ? 與 : 隔開。
第 1 個運算元必須是一個布林值 , 如果這個布林值
為 true, 就選取第 2 個運算元進行運算 , 否則就選
取第 3 個運算元進行運算 , 作為整個條件運算的結
果。
例如。
125. 條件運算子 (Conditional Operator)
其中 , 第 4 行使用了條件運算子來設定變數 i 的值
, 3 個運算元分別是 j % 2 == 1 這個比較運算
式、 2 、以及 1 。
意思就是如果 j % 2 == 1 成立的話 , 就將變數 i 內
容設為 2, 否則就為 1 。
由於這裡 j 是 17, 所以設立的條件會成立 , 於是變
數 i 變成 2 了。
127. 條件運算子的邊際效應
程式第 4 行執行時 , 由於第 3 個運算元 j++ 並非
被選取的運算元 , 所以 j++ 並不會執行 , 因此變數 j
的內容還是 17 。
128. 條件運算子運算結果的型別
使用條件運算子時 , 有一個陷阱很容易忽略 , 那就
是條件運算子運算結果的型別判定。條件運算子依
據的規則如下:
1. 如果後 2 個運算元的型別相同 , 那麼運算結果的型
別也就一樣。
2. 如果第 2 個運算元是 byte 型別 , 而第 3 個運算元
是 short 型別 , 運算結果就是 short 型別。
129. 條件運算子運算結果的型別
3. 如果第 2 個運算元是 byte 、 short 、或是 char
型別 , 而第 3 個運算元是個由字面常數構成的運算
式 , 並且運算結果為 int, 而且落於第 2 個運算元型
別的數值範圍內 , 那麼條件運算子的運算結果就和第
2 個運算元型別相同。
4. 如果以上條件均不符合 , 那麼運算結果的型別就依據
二元運算子的運算元自動提升規則。
▪
請熟悉這些規則 , 以免被一些簡單的考題給難倒。
131. 條件運算子運算結果的型別
第 5 行看起來並沒有甚麼不對 , 但是實際上連編譯
都無法通過。
這是因為依據剛剛的規則 , 這一行中的條件運算子會
因為第 3 個運算元 i 是 int 型別 , 使得運算結果變
成 int 型別 , 當然就無法放入 byte 型別的 b 中了
。
此時 , 只要把運算結果強制轉型為 byte 型別 , 就可
以正常執行了。
132. 1. Given the following code :
What is the result?
A. (i == j) is false
B. (i == j) is true
C. Compilation fails at line 4 D. Compilation fails at line 6
E. There is a runtime error
133. 2. Given the following code:
What is the result ?
A. Output: 558
B. Output: 5553
C. Compilation fails at line 3
D. Compilation fails at line 4
E. There is a runtime error
134. 3. Given the following code:
Which, inserted at line 4, will compile correctly? (Choose all
that apply.)
A. float x = 128.5;
B. double x = s.length();
C. byte x = (byte) 120 + 1;
D. short x = s.length();
E. int x = 255L;
136. 4. Given the following code:
What is the result?
A. s = 1236
D. Compilation fails
B. s = 1237
C. s = 1238
E. Runtime error
137. 5. Given the following code:
What is the result?
A. 245
B. 321
C. 323
D. 333
E. 345
138. 6. Given the following code:
What is the printed value of j?
A. 14
B. -14
C. 15
D. -15
E. An error at line 3 causes compilation fails
139. 7. Which two of following declarations are illegal?
A. S1
B. S2
C. S3
D. S4
E. S5
F. S6