前言
大部分運算子是以符號而成的指令,少數運算子使用文字。由於運算子無法拆解成更小的指令,可以視為程式語言的基本指令。本文介紹 Java 的運算子。
自製斷言 (Assertion)
有些程式設計教材會將運算結果印在終端機上,再以人工判讀。但這樣的模式未充份利用電腦自動化的優點。故本文會改用斷言來自動判斷運算結果是否正確。
其實 Java 有原生的斷言。但為了相容於舊程式碼,預設不開啟斷言。為了避免讀者在練習時忘了加上相關參數,此處不使用原生斷言,而是自己手刻一個簡易版的斷言函式。
加入斷言函式的樣板程式碼如下:
public class MainProgram
{
public static void main (String[] args)
{
/* Implement your code here. */
}
/* A home-made assert. */
public static void assertCond (boolean cond)
{
if (!cond)
throw new IllegalArgumentException("Wrong condition");
}
/* A home-made assert with a custom statement. */
public static void assertCond (boolean cond, String stat)
{
if (!cond)
throw new IllegalArgumentException(stat);
}
}
斷言並不是 Java 特有的概念,所以自製斷言並非不可行。所謂斷言是在目標情境失敗時發出中斷程式的指令。以 Java 來說,就是拋出例外 (exception)。
這裡製作兩個同名異參的斷言函式,第二個版本的斷言可放入自行設置的斷言敘述。這裡用到函式重載 (function overloading) 的特性,所以兩個函式名稱相同。
雖然 Java 是物件導向語言,如果在 Java 中要寫指令式 (imperative) 程式的話,使用靜態函式 (static function) 即可。靜態函式是因為 Java 直接把物件導向範式融入語法才產生的概念,其實就和 C、C++ 的一般函式無異。
算術運算子 (Arithmetic Operator)
以下是常見的一元算術運算子:
+
:取正數-
:取負數
以下則是常見的二元算術運算子:
+
:相加-
:相減*
:相乘/
:相除%
:取餘數
以下是範例程式:
public class MainProgram
{
public static void main (String[] args)
{
assertCond(7 + 3 == 10);
assertCond(7 - 3 == 4);
assertCond(7 * 3 == 21);
assertCond(7 / 3 == 2);
assertCond(7 % 3 == 1);
}
/* Home-made assert. */
public static void assertCond (boolean cond)
{
if (!cond)
throw new IllegalArgumentException("Wrong condition");
}
}
遞增、遞減運算子 (Increment and Decrement Operator)
遞增、遞減運算子是大部分 C 家族語言都有的算術運算子。這個運算子主要是用在迴圈 (loop) 的計數器 (counter)。包括以下兩種運算子:
++
:遞增--
:遞減
注意這兩個運算子隱含著狀態改變。前綴時先改變狀態再回傳值,後綴時先回傳值再改變狀態。像是以下的範例程式:
public class MainProgram
{
public static void main (String[] args)
{
var n = 3;
assertCond(n++ == 3);
assertCond(n == 4);
n = 3;
assertCond(++n == 4);
assertCond(n == 4);
}
/* Home-made assert. */
public static void assertCond (boolean cond)
{
if (!cond)
throw new IllegalArgumentException("Wrong condition");
}
}
要避免混淆的話,不要在同一行敘述內執行太多動作。
二元運算運算子 (Bitwise Operator)
以下是二元運算所用的運算子:
&
:bitwise and^
:bitwise xor|
:bitwise or~
:取補數>>
:右移<<
:左移
以下是範例程式。由於二元運算在日常生活中少用,此處加上註解:
public class MainProgram
{
public static void main (String[] args)
{
/* 3 is 0011
5 is 0101 */
/* 0011
&) 0101
---------
0001 */
assertCond((3 & 5) == 1, "3 & 5 should be 1");
/* 0011
|) 0101
---------
0111 */
assertCond((3 | 5) == 7, "3 | 5 should be 7");
/* 0011
^) 0101
---------
0110 */
assertCond((3 ^ 5) == 6, "3 ^ 5 should be 6");
/* <<) 0000 0101
---------------
0000 1010 */
assertCond((5 << 1) == 10, "5 << 1 should be 10");
/* >>) 0000 0101
---------------
0000 0010 */
assertCond((5 >> 1) == 2, "5 >> 1 should be 2");
}
/* Home-made assert with custom statement. */
public static void assertCond (boolean cond, String stat)
{
if (!cond)
throw new IllegalArgumentException(stat);
}
}
字串相接運算子 (String Concatenation Operator)
雖然 Java 不支援運算子重載,Java 重載了 +
運算子,用來相接字串。以下是範例程式:
public class MainProgram
{
public static void main (String[] args)
{
var greeting = "Hello";
var user = "Michelle";
assertCond((greeting + " " + user).equals("Hello Michelle"));
}
/* Home-made assert. */
public static void assertCond (boolean cond)
{
if (!cond)
throw new IllegalArgumentException("Wrong condition");
}
}
關係運算子 (Relational Operator)
關係運算子用來比較兩值間的相對關係。以下是 Java 的關係運算子:
==
:相等!=
:不相等>
:大於>=
:大於或等於<
:小於<=
:小於或等於
以下是範例程式:
public class MainProgram
{
public static void main (String[] args)
{
assertCond(3 == 3);
assertCond(3 != 7);
assertCond(7 > 3);
assertCond(7 >= 3);
assertCond(3 < 7);
assertCond(3 <= 7);
}
/* Home-made assert. */
public static void assertCond (boolean cond)
{
if (!cond)
throw new IllegalArgumentException("Wrong condition");
}
}
邏輯運算子 (Logical Operator)
以下是 Java 的邏輯運算子:
&
:logical and&&
:logical and。有短路^
:logical xor|
:logical or||
:logical or。有短路!
:logical not
以下是範例程式:
public class MainProgram
{
public static void main (String[] args)
{
/* AND */
assertCond((true & true) == true);
assertCond((true & false) == false);
assertCond((false & true) == false);
assertCond((false & false) == false);
/* OR */
assertCond((true | true) == true);
assertCond((true | false) == true);
assertCond((false | true) == true);
assertCond((false | false) == false);
/* XOR */
assertCond((true ^ true) == false);
assertCond((true ^ false) == true);
assertCond((false ^ true) == true);
assertCond((false ^ false) == false);
/* NOT */
assertCond((!true) == false);
assertCond((!false) == true);
}
/* Home-made assert. */
public static void assertCond (boolean cond)
{
if (!cond)
throw new IllegalArgumentException("Wrong condition");
}
}
在有短路的情形下,前半部的指令滿足條件了,後半部的指令則不會執行。例如,a && b
中,當 a
是 false
等效值時,該運算必定為 false
,b
就沒有執行的必要,這時候 Java 會跳過 b
。將這個概念寫成實例如下:
public class MainProgram
{
public static void main (String[] args)
{
try {
if (false && bang()) { /* Pass. */ }
}
catch (Exception e) {
System.err.println(e);
}
}
/* A bomb. */
public static Boolean bang () throws Exception
{
throw new Exception("Something wrong");
}
}
這個例子比較難,因為用到先前未提到的例外處理 (exception handling)。簡單地說,這個程式利用短路的機制,所以不會拋出例外。
同理,使用 ||
的短路範例如下:
public class MainProgram
{
public static void main (String[] args)
{
try {
if (true || bang()) { /* Pass. */ }
}
catch (Exception e) {
System.err.println(e);
}
}
/* A bomb. */
public static Boolean bang () throws Exception
{
throw new Exception("Something wrong");
}
}
除了前述運算子外,還可以用三元運算子來取值。像是以下的範例程式:
public class MainProgram
{
public static void main (String[] args)
{
var a = 7;
var b = 3;
assertCond((a > b ? a : b) == 7);
}
/* Home-made assert. */
public static void assertCond (boolean cond)
{
if (!cond)
throw new IllegalArgumentException("Wrong condition");
}
}
三元運算子類似於 if
敘述,但該運算子是表達式,所以可以直接寫在一行指令內。
指派運算子 (Assignment Operator)
簡單指派運算子 =
單純起著賦值的作用,我們已經在先前的範例看過了。
複合指派運算子則是一種語法糖。原本的指派運算如下:
var a = 3;
a = a + 4;
改用複合指派運算如下:
var a = 3;
a += 4;
以下是 Java 的複合指派運算子:
+=
:相加後賦值-=
:相減後賦值*=
:相乘後賦值/=
:相除後賦值%=
:取餘數後賦值&=
:進行 bitwise and 運算後賦值^=
:進行 bitwise xor 運算後賦值|=
:進行 bitwise or 運算後賦值>>=
:右移後賦值<<=
:左移後賦值
型態轉換運算子 (Casting Operator)
型態轉換運算子為一對 (
和 )
所組成。將目標型態放在括號中即可。以下是範例程式:
public class MainProgram
{
public static void main (String[] args)
{
assertCond(((int) 3.9999) == 3);
}
/* Home-made assert. */
public static void assertCond (boolean cond)
{
if (!cond)
throw new IllegalArgumentException("Wrong condition");
}
}
3.9999
轉換型態至 int
後即為 3
,故兩者相等。
型態比較運算子
型態比較運算子用來檢查物件是否屬於特定類別。Java 的型態比較運算子為 instanceof
。
以下範例程式碼超出一點點目前的範圍,請讀者試著閱讀一下:
import java.util.ArrayList;
import java.util.List;
public class MainProgram
{
public static void main (String[] args)
{
/* Create an `ArrayList` object. */
List<Integer> lst = new ArrayList<Integer>();
/* Check whether the object belongs to `List`. */
assertCond(lst instanceof List);
}
/* Home-made assert. */
public static void assertCond (boolean cond)
{
if (!cond)
throw new IllegalArgumentException("Wrong condition");
}
}
前一行敘述建立 ArrayList
型態的物件 lst
。後一行敘述確認 lst
是否屬於 List
。
雖然這裡只有兩行敘述,但這兩行敘述包含一些目前沒有的觀念。讀者先略讀即可。後文會再談。
運算子優先順序 (Operator Precedence)
在同一行敘述使用多個運算子的話,會按照運算子優先順序來決定先執行那個運算子。可到這裡觀看 Java 的運算子優先順序。
實際上,不太需要背誦運算子優先順序。因為運算子優先順序和代數運算的順序相同,而且用括號可以手動改變運算子優先順序。