運算子
May 20, 2022程式目的簡單地說就是運算,除了運算還是運算,程式語言中提供運算功能的就是運算子(Operator)。
算術運算
與算術相關的運算子 +
、-
、*
、/
,也就是加、減、乘、除這類運算子,另外 %
,稱為模數運算子或餘除運算子。算術運算子使用上與你學過的加減乘除一樣,也是先乘除後加減。例如以下程式碼片段會在文字模式下顯示 7:
System.out.println(1 + 2 * 3);
以下程式碼會顯示的是 6:
System.out.println(2 + 2 + 8 / 4);
如果你想要的是 2 + 2 + 8 加總後,再除以 4,請加上括號表示運算先後順序。例如以下程式碼顯示的是 3:
System.out.println((2 + 2 + 8) / 4);
%
運算子計算的結果是除法後的餘數,例如 10 % 3
會得到餘數 1;一個使用 %
的例子是數字循環,假設有個立方體要進行 360 度旋轉,每次要在角度上加 1,而 360 度後必須復歸為0重新計數,這時可以這麼撰寫:
var count = 0;
....
count = (count + 1) % 360;
在運算子的兩邊各留一個空白,這樣比較容易閱讀。
比較、條件運算
數學上有大於、等於、小於的比較運算,Java 也提供了這些運算子,這些運算子稱之為比較運算子(Comparison operator),它們有大於(>
)、不小於(>=
)、小於(<
)、不大於(<=
)、等於(==
)以及不等於(!=
),比較條件成立時以 boolean
型態 true
表示,比較條件不成立時以 false
表示。以下程式片段示範了幾個比較運算的使用:
package cc.openhome;
public class Comparison {
public static void main(String[] args) {
System.out.printf("10 > 5 結果 %b%n", 10 > 5);
System.out.printf("10 >= 5 結果 %b%n", 10 >= 5);
System.out.printf("10 < 5 結果 %b%n", 10 < 5);
System.out.printf("10 <= 5 結果 %b%n", 10 <= 5);
System.out.printf("10 == 5 結果 %b%n", 10 == 5);
System.out.printf("10 != 5 結果 %b%n", 10 != 5);
}
}
程式的執行如下所示:
10 > 5 結果 true
10 >= 5 結果 true
10 < 5 結果 false
10 <= 5 結果 false
10 == 5 結果 false
10 != 5 結果 true
==
是兩個連續的 =
組成,而不是一個 =
,一個 =
是指定運算,這點必須特別注意。例如若變數 x
與 y
要比較是否相等,應該是寫成 x == y
,而不是寫成 x = y
,後者作用是將 y
的值指定給 x
。對於類別型態宣告的參考名稱來說,兩個參考名稱使用 ==
比較時,是比較兩個名稱是否參考至同一物件。
Java 有個條件運算子(Conditional operator),傳回值依條件式結果而定,如果條件式結果為 true
,則傳回:前的值,若為 false
,則傳回:後的值。例如,若 score
是 int
宣告,儲存了使用者輸入的學生成績,以下程式片段可用來判斷學生是否及格:
System.out.printf("該生是否及格?%c%n", score >= 60 ? '是' : '否');
條件運算子使用適當的話可以少寫幾句程式碼。例如,若 number
是 int
宣告,儲存使用者輸入的數字,則以下程式片段可判斷奇數或偶數:
System.out.printf("是否為偶數?%c%n", (number % 2 == 0) ? '是' : '否');
邏輯運算
在邏輯上有 所謂的「且」(AND)、「或」(OR)與「反相」(NOT),Java 也提供對應的邏輯運算子(Logical operator),分別為 &&
(AND)、||
(OR)及 !
(NOT)。看看以下的程式片段會輸出什麼結果?
var number = 75;
System.out.println(number > 70 && number < 80);
System.out.println(number > 80 || number < 75);
System.out.println(!(number > 80 || number < 75));
三段陳述句分別會輸出 true
、false
與 true
三種結果,分別表示 number
大於 70 且小於 80 為真、number
大於 80 或小於 75 為假、number
大於 80 或小於 75 的相反為真。
&&
與 ||
有所謂捷徑運算(Short-Circuit Evaluation)。因為 AND 只要其中一個為假,就可以判定結果為假,對 &&
來說,只要左運算元(Operand)評估為 false
,就會直接傳回 false
,不會再去運算右運算元。因為 OR 只要其中一個為真,就可以判定結果為真,對 ||
來說,只要左運算元評估為 true
,就會直接傳回 true
,就不會再去運算右運算元。
來舉個運用捷徑運算的例子,在 Java 兩個整數相除,若除數為 0 會發生 ArithmeticException
,代表除0的錯誤,以下運用 &&
捷徑運算避免了這個問題:
if(b != 0 && a / b > 5) {
// 做一些事...
}
在這個程式片段中,變數 a
與 b
都是 int
型態,如果 b
為 0 的話,&&
左邊運算元結果就是 false
,直接判斷整個&&的結果應是 false
,不用再去評估右運算元,從而避免了a /b
而 b
等於 0 時的除零錯誤。
位元運算
在數位設計上有 AND、OR、NOT、XOR 與補數運算,Java 提供對應的運位元運算子(Bitwise Operator),分別是 &
(AND)、|
(OR)、^
(XOR)與 ~
(補數)。如果不會基本位元運算,可以從以下範例瞭解各個位元運算結果:
package cc.openhome;
public class Bitwise {
public static void main(String[] args) {
System.out.println("AND運算:");
System.out.printf("0 AND 0 %5d%n", 0 & 0);
System.out.printf("0 AND 1 %5d%n", 0 & 1);
System.out.printf("1 AND 0 %5d%n", 1 & 0);
System.out.printf("1 AND 1 %5d%n", 1 & 1);
System.out.println("\nOR運算:");
System.out.printf("0 OR 0 %6d%n", 0 | 0);
System.out.printf("0 OR 1 %6d%n", 0 | 1);
System.out.printf("1 OR 0 %6d%n", 1 | 0);
System.out.printf("1 OR 1 %6d%n", 1 | 1);
System.out.println("\nXOR運算:");
System.out.printf("0 XOR 0 %5d%n", 0 ^ 0);
System.out.printf("0 XOR 1 %5d%n", 0 ^ 1);
System.out.printf("1 XOR 0 %5d%n", 1 ^ 0);
System.out.printf("1 XOR 1 %5d%n", 1 ^ 1);
}
}
執行結果就是各個位元運算的結果:
AND運算:
0 AND 0 0
0 AND 1 0
1 AND 0 0
1 AND 1 1
OR運算:
0 OR 0 0
0 OR 1 1
1 OR 0 1
1 OR 1 1
XOR運算:
0 XOR 0 0
0 XOR 1 1
1 XOR 0 1
1 XOR 1 0
位元運算是逐位元運算,例如 10010001 與 01000001 做 AND 運算,是一個一個位元對應運算,答案就是 00000001。補數運算是將所有位元 0 變 1,1 變 0。例如 00000001 經補數運算就會變為 11111110。例如:
byte number = 0;
System.out.println(~number);
上面的程式片段會顯示 -1,因為 byte
佔記憶體一個位元組,number
儲存的 0 在記憶體中是的位元 00000000,經補數運算就變成 11111111,這個數在電腦中用整數表示則是 -1。
邏輯運算子與位元運算子也是很常被混淆的,像是 &&
與 &
,||
與 |
,初學時可得多注意。
在位元運算上,Java 還有左移(<<
)與右移(>>
)兩個運算子,左移運算子會將所有位元往左移指定位數,左邊被擠出去的位元會被丟棄,而右邊補上 0;右移運算則是相反,會將所有位元往右移指定位數,右邊被擠出去的位元會被丟棄,至於最左邊補上原來的位元,如果左邊原來是 0 就補 0,1 就補 1。還有個 >>>
運算子,這個運算子在右移後,最左邊一定是補 0。
使用左移運算來作簡單的2次方運算示範:
package cc.openhome;
public class Shift {
public static void main(String[] args) {
var number = 1;
System.out.printf( "2 的 0 次方: %d%n", number);
System.out.printf( "2 的 1 次方: %d%n", number << 1);
System.out.printf( "2 的 2 次方: %d%n", number << 2);
System.out.printf( "2 的 3 次方: %d%n", number << 3);
}
}
執行結果:
2 的 0 次方: 1
2 的 1 次方: 2
2 的 2 次方: 4
2 的 3 次方: 8
實際來左移看看就知道為何可以如此作次方運算了:
00000001 -> 1
00000010 -> 2
00000100 -> 4
00001000 -> 8
遞增、遞減運算
在程式中對變數遞增1或遞減1是很常見的運算,例如:
var i = 0;
i = i + 1;
System.out.println(i);
i = i - 1;
System.out.println(i);
這個程式片段會分別顯示出 1 與 0 兩個數,你可以使用遞增、遞減運算子來撰寫程式:
var i = 0;
i++;
System.out.println(i);
i--;
System.out.println(i);
那麼哪個寫法比較好呢?就簡潔度而言,使用 ++
、--
的寫法比較好,就效率而言,其實沒差,因為如果你寫 i = i + 1
,編譯器會自動幫你改成 i++
,同樣地,如果你寫 i = i – 1
,編譯器會自動幫你改為 i--
。
上面的程式片段還可以再簡潔一些:
var i = 0;
System.out.println(++i);
System.out.println(--i);
可以將++或–運算子撰寫在變數的前或後,不過兩種寫法有差別,將 ++
或 --
運算子寫在變數前,表示先將變數值加或減 1,然後再傳回變數值,將 ++
或 --
運算子寫在變數後,表示先傳回變數值,然後再對變數加或減 1。例如:
var i = 0;
var number = 0;
number = ++i; // 結果相當於i = i + 1; number = i;
System.out.println(number);
number = --i; // 結果相當於i = i - 1; number = i;
System.out.println(number);
在這個程式片段中,number
的值會前後分別顯示為 1 與 0。再來看個例子:
var i = 0;
var number = 0;
number = i++; // 相當於number = i; i = i + 1;
System.out.println(number);
number = i--; // 相當於 number = i; i = i - 1;
System.out.println(number);
在這個程式片段中,number
的值會前後分別顯示為 0 與 1。
指定運算
到目前為止只看過一個指定運算子,也就是 =
這個運算子,事實上指定運算子還有以下幾個:
指定運算子 | 範例 | 結果 |
---|---|---|
+= | a += b | a = a + b |
-= | a -= b | a = a - b |
*= | a *= b | a = a * b |
/= | a /= b | a = a / b |
%= | a %= b | a = a % b |
&= | a &= b | a = a & b |
= | a |= b | a = a | b |
^= | a ^= b | a = a ^ b |
<<= | a <<= b | a = a << b |
>>= | a >>= b | a = a >> b |
>»= | a >»= b | a = a >» b |