位元詩人 [Java] 程式設計教學:運算子 (Operator)

Java運算子
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

大多數運算子是由符號組成的指令,少數則以文字形式呈現。由於運算子無法再拆解為更小的單位,因此可視為程式語言中的基本指令。本文將介紹 Java 的各類運算子。

自製斷言 (Assertion)

有些程式設計教材會將運算結果輸出至終端機,再由人為判讀是否正確。然而,這種方式未能充分發揮電腦在自動化上的優勢。因此,本文改採斷言機制,自動檢驗運算結果的正確性。

事實上,Java 本身提供原生的斷言功能。不過,為了相容舊有程式碼,其預設並未啟用。為避免讀者在練習時遺漏相關啟用參數,本文不使用原生斷言,而是自行實作一個簡易版本的斷言函式。

加入斷言函式的樣板程式碼如下:

void main()
{
    /* Implement your code here. */
}

/* A home-made assert. */
void assertCond(boolean cond)
{
    if (!cond)
        throw new IllegalArgumentException("Wrong condition");
}

/* A home-made assert with a custom statement. */
void assertCond(boolean cond, String stat)
{
    if (!cond)
        throw new IllegalArgumentException(stat);
}

斷言並非 Java 所特有的概念,因此自行實作並無問題。所謂斷言,是在目標條件不成立時中斷程式執行的機制。在 Java 中,通常是透過拋出例外(exception)來達成。

此處實作了兩個同名但參數不同的斷言函式,其中第二個版本可傳入自訂的錯誤訊息。這裡運用了函式重載(function overloading)的特性,因此兩個函式可以使用相同名稱。

算術運算子 (Arithmetic Operator)

以下是常見的一元算術運算子:

  • +:取正數
  • -:取負數

以下則是常見的二元算術運算子:

  • +:相加
  • -:相減
  • *:相乘
  • /:相除
  • %:取餘數

以下是範例程式碼片段:

assertCond(7 + 3 == 10);
assertCond(7 - 3 == 4);
assertCond(7 * 3 == 21);
assertCond(7 / 3 == 2);
assertCond(7 % 3 == 1);

遞增、遞減運算子 (Increment and Decrement Operator)

遞增、遞減運算子是大部分 C 家族語言都有的算術運算子。這個運算子主要是用在迴圈 (loop) 的計數器 (counter)。包括以下兩種運算子:

  • ++:遞增
  • --:遞減

注意這兩個運算子隱含著狀態改變。前綴時先改變狀態再回傳值,後綴時先回傳值再改變狀態。像是以下的範例程式碼片段:

var n = 3;
assertCond(n++ == 3);
assertCond(n == 4);

n = 3;
assertCond(++n == 4);
assertCond(n == 4);

要避免混淆的話,不要在同一行敘述內執行太多動作。

二元運算運算子 (Bitwise Operator)

以下是二元運算所用的運算子:

  • &:bitwise and
  • ^:bitwise xor
  • |:bitwise or
  • ~:取補數
  • >>:右移
  • <<:左移

以下是範例程式碼片段。由於二元運算在日常生活中少用,此處加上註解:

/* 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");

字串相接運算子 (String Concatenation Operator)

雖然 Java 不支援運算子重載,Java 重載了 + 運算子,用來相接字串。以下是範例程式碼片段:

var greeting = "Hello";
var user = "Lyra";

assertCond((greeting + " " + user).equals("Hello Lyra"));

關係運算子 (Relational Operator)

關係運算子用來比較兩值間的相對關係。以下是 Java 的關係運算子:

  • ==:相等
  • !=:不相等
  • >:大於
  • >=:大於或等於
  • <:小於
  • <=:小於或等於

以下是範例程式碼片段:

assertCond(3 == 3);
assertCond(3 != 7);
assertCond(7 > 3);
assertCond(7 >= 3);
assertCond(3 < 7);
assertCond(3 <= 7);

邏輯運算子 (Logical Operator)

以下是 Java 的邏輯運算子:

  • &:logical and
  • &&:logical and。有短路
  • ^:logical xor
  • |:logical or
  • ||:logical or。有短路
  • !:logical not

以下是範例程式碼片段:

/* 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);

在有短路的情形下,前半部的指令滿足條件了,後半部的指令則不會執行。例如,a && b 中,當 afalse 等效值時,該運算必定為 falseb 就沒有執行的必要,這時候 Java 會跳過 b。將這個概念寫成實例如下:

void main()
{
   try {
       if (false && bang()) { /* Pass. */ }
   }
   catch (Exception e) {
       System.err.println(e);
   }
}

/* A bomb. */
Boolean bang() throws Exception
{
    throw new Exception("Something wrong");
}

這個例子比較難,因為用到先前未提到的例外處理 (exception handling)。簡單地說,這個程式利用短路的機制,所以不會拋出例外。

同理,使用 || 的短路範例如下:

void main()
{
   try {
       if (true || bang()) { /* Pass. */ }
   }
   catch (Exception e) {
       System.err.println(e);
   }
}

/* A bomb. */
Boolean bang() throws Exception
{
    throw new Exception("Something wrong");
}

除了前述運算子外,還可以用三元運算子來取值。像是以下的範例程式:

var a = 7;
var b = 3;

assertCond((a > b ? a : b) == 7);

三元運算子類似於 if 敘述,但該運算子是表達式,所以可以直接寫在一行指令內。

指派運算子 (Assignment Operator)

簡單指派運算子 = 單純起著賦值的作用,我們已經在先前的範例看過了。

複合指派運算子則是一種語法糖。原本的指派運算如下:

var a = 3;
a = a + 4;

改用複合指派運算如下:

var a = 3;
a += 4;

以下是 Java 的複合指派運算子:

  • +=:相加後賦值
  • -=:相減後賦值
  • *=:相乘後賦值
  • /=:相除後賦值
  • %=:取餘數後賦值
  • &=:進行 bitwise and 運算後賦值
  • ^=:進行 bitwise xor 運算後賦值
  • |=:進行 bitwise or 運算後賦值
  • >>=:右移後賦值
  • <<=:左移後賦值

型態轉換運算子 (Casting Operator)

型態轉換運算子為一對 () 所組成。將目標型態放在括號中即可。以下是範例程式:

assertCond(((int) 3.9999) == 3);

3.9999 轉換型態至 int 後即為 3,故兩者相等。

型態比較運算子

型態比較運算子用來檢查物件是否屬於特定類別。Java 的型態比較運算子為 instanceof

以下範例程式碼超出一點點目前的範圍,請讀者試著閱讀一下:

/* Create an `ArrayList` object. */
List<Integer> lst = new ArrayList<Integer>();
/* Check whether the object belongs to `List`. */
assertCond(lst instanceof List);

前一行敘述建立 ArrayList 型態的物件 lst。後一行敘述確認 lst 是否屬於 List

雖然這裡只有兩行敘述,但這兩行敘述包含一些目前沒有的觀念。讀者先略讀即可。後文會再談。

運算子優先順序 (Operator Precedence)

在同一行敘述使用多個運算子的話,會按照運算子優先順序來決定先執行那個運算子。可到這裡觀看 Java 的運算子優先順序。

實際上,不太需要背誦運算子優先順序。因為運算子優先順序和代數運算的順序相同,而且用括號可以手動改變運算子優先順序。

關於作者

位元詩人 (ByteBard) 是資訊領域碩士,喜歡用開源技術來解決各式各樣的問題。這類技術跨平台、重用性高、技術生命長。

除了開源技術以外,位元詩人喜歡日本料理和黑咖啡,會一些日文,有時會自助旅行。

近期在學習韓文,並將語言學習的心得轉化為開源專案,回饋社群。

這裡是位元詩人的 GitHub 個人頁