Michelle Chen [Java] 程式設計教學:運算子 (Operator)

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

前言

大部分運算子是以符號而成的指令,少數運算子使用文字。由於運算子無法拆解成更小的指令,可以視為程式語言的基本指令。本文介紹 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 中,當 afalse 等效值時,該運算必定為 falseb 就沒有執行的必要,這時候 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 的運算子優先順序。

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