Ch.5 運算子

5-1 算術運算子

程式會執行從簡單到複雜等難度不一的數學計算,數學中有加(+)、減(-)、乘(*)、除(-)等基本的運算,在程式語言中對應這些運算作用的符號稱之為算術運算子( Arithmetic operator )

在這個小節中將區分三個主題來討論算術運算子:

  • 標準數學運算子
  • 遞增與遞減運算子
  • 算術運算與晉升、轉型的問題

• 標準數學運算子

標準的數學運算子,通常稱為二元運算 ( Binary operators ),因為在運算子的左右都必須提供運算元( Operand )方可進行運算,例如:

System.out.println(1 + 2);
                            

+表示加法運算子,兩邊提供的 1 與 2 數值即運算元。在 Java 程式語言中所使用到的數學運算子列於表 5-1 內。

用途運算子範例
加法+num1 + num2
若 num1 等於 10、num2 等於 2,運算結果等於 12。
減法-num1 - num2
若 num1 等於 10、num2 等於 2,運算結果等於 8。
乘法*num1 * num2
若 num1 等於 10、num2 等於 2,運算結果等於 20。
除法/num1 / num2
若 num1 等於 10、num2 等於 2,運算結果等於 5。
模數(餘除) %num1 % num2
若 num1 等於 31、num2 等於 6,運算結果等於 1。
表 5-1 數學運算子

模數運算就是取兩數相乘後的餘數;程式碼 5-1 示範了如何如何宣告變數並指定值,之後使用數學運算子對變數進行各種數學運算。

public class ArithmeticDemo {
    public static void main(String[] args) {
        // 宣告變數與值
        int x = 10;
        int y = 5;

        System.out.println("變數值...");
        System.out.println(" x = " + x);
        System.out.println(" y = " + y);

        // 加法示範
        System.out.println("加法...");
        System.out.println("x + y = " + (x + y));

        // 減法示範
        System.out.println("減法...");
        System.out.println("x - y = " + (x - y));

        // 乘法示範
        System.out.println("乘法...");
        System.out.println("x * y = " + (x * y));

        // 除法示範
        System.out.println("除法...");
        System.out.println("x / y = " + (x / y));

        // 模數運算
        System.out.println("計算餘數...");
        System.out.println("x % y = " + (x % y));
    }
}
                            
程式碼 5-1 ArithmeticDemo.java

在使用 println()進行輸出時,也可以使用+來串接想要輸出的內容,而您也可以看到程式中在運算式上加上了()括號,這表示您要先執行 x 與 y 運算,再將結果輸出,程式的編譯與執行結果如圖 5-1 所示:

圖5-1、程式碼 5-1 的編譯與執行結果

如果要將運算的結果指定給某變數,則使用指定運算子( Assignment operator )=,例如:

int x = 10;
int y = 5;
int sum = x + y;
System.out.println(sum);
                            

在第 3 行中,x 與 y 會先相加,之後再將運算結果指定給sum,所以最後會顯示 15。

• 遞增與遞減運算子

將變數值加 1 或減 1 在程式中是很常見的指令。您可以簡單的使用加法運算子(+)來達成,例如:

int age = 10;
age = age + 1;
System.out.println(age); // 顯示 11
                            

然而遞增與遞減 1 個單位是常見的動作,所以有特定的章節運算子( Unary operators )來表示:++與—運算子。之所以稱為章節運算子,是因為運算時只需提供一個運算元,例如下頁的程式片段也是顯示 11:

int age = 10;
age++;
System.out.println(age); // 顯示 11
                            

++運算子稱之為( Increment operator )遞增運算子,而遞減運算子( Decrement operator )則是--,作用是將指定的變數值減 1,例如:

int age = 10;
age--;
System.out.println(age); // 顯示 9
                            

像++與--這些運算子可以放在變數的前面(前置遞增及前置遞減)或後面(後置遞增及後置遞減),例如:

int age = 10;
++age;
System.out.println(age); // 顯示 11
--age;
System.out.println(age); // 顯示 10
                            

事實上遞增與遞減運算子的前置與後置在使用時,兩個的作用是有差別的,使用後置遞增( Post-increment )後置遞減( Post-decrement )時,必須一直等到這行程式碼的其它運算都執行後才作遞增與遞減的動作。所以假使此運算子在前面(Prefix),那這個運算會在運算式執行之前就做遞增或遞減的動作。而假使此運算子在後面(Post),那這個運算會在原來的數值被使用後才做遞增或遞減的動作。

舉個實際的例子來看,如果遞增運算子與指定運算子同時使用時,看看程式碼 5-2 會輸出什麼結果:

public class UnaryOperatorDemo {
    public static void main(String[] args) {
        int age = 10;
        int var = age++;
        System.out.println(age);
        System.out.println(var);
    }
}
                            
程式碼 5-2 UnaryOperatorDemo.java

由於 age++是後置的寫法,所以 age 的值會先指定給 var,之後 age 才進行遞增的動作,也就是說程式中的第 4 行,其實相當於:

var = age;
age = age + 1;
                            

所以範例 5-2 的編譯與執行結果會如下所示:

圖5-2、程式碼 5-2 的編譯與執行結果

如果改變程式碼 5-2 為前置寫法,那麼結果會是如何呢?

public class UnaryOperatorDemo2{
    public static void main(String[] args){
        int age = 10;
        int var = ++age;
        System.out.println(age);
        System.out.println(var);
    }
}
                            
程式碼 5-3 UnaryOperatorDemo2.java

由於++age 是前置的寫法,所以 age 的值會先遞增,之後再指定給 var,也就是說程式中的第 4 行,其實相當於:

age = age + 1;
var = age;
                            

所以範例 5-3 的編譯與執行結果會如下所示:

圖5-3、程式碼 5-3 的編譯與執行結果

• 運算子的優先順序

或許您已經注意到程式在處理運算時,對於運算子的處理是有其先後順序的,在複雜數學表示式中,同一行中有多個運算子,電腦應該如何選擇最先使用的運算子呢?

運算子優先順序的規則

為了讓數學運算一致,Java 使用底下的標準數學運算規則來做為運算子優先順序的規則:

  • 有一對圓括弧的運算子
  • 遞增及遞減運算子
  • 乘法與除法運算子是由左而右做計算
  • 加法與減法運算子是由左而右做計算

假如一樣優先權的運算子在一個陳述句中連續地出現,則它們都是由左而右做計算。

需要運算子優先順序規則的範例

底下的範例驗證了運算子優先順序規則存在的必要性:

c = 25 – 5 * 4 / 2 – 10 + 4;
                            

不管是從數學表示式或者是從任何所提供的註解,程式所表達的意思都不很明確,如果根據優先順序的規則(由圓括弧所指),其計算方式應該如下:

c = 25 – ((5 * 4) / 2) – 10 + 4;
                            

您的數學運算式會以優先順序的規則做計算;然而,使用圓括弧來說明您所想要表達的意思是個不錯的方式,例如:

c = (((25 – 5) * 4) / (2 – 10)) + 4;
c = ((20 * 4 / (2 – 10)) + 4;
c = (80 / (2 – 10)) + 4;
c = (80 / –8) + 4;
c = -10 + 4;
c = -6;
                            

• 算術運算與晉升、轉型的問題

在進行算術運算時,需注意資料型態的問題,先來看看下面這個程式片段會顯示什麼結果?

System.out.println(10 / 3);
                            

這個程式片段會在螢幕上顯示 3,Java 程式運行時出錯了嗎?不是的!在解釋之前,再來看一個程式片段:

System.out.println(10.0 / 3);
                            

這次將被除數改以浮點數的方式撰寫,進行除法時,程式就顯示正確的值了,或許聰明的您已經想到,當您在程式中直接寫下 10.0 時,編譯器會自動配給它 double 型態的記憶體空間,跟這個有關嗎?

答案是正確的!在進行算術運算時,必須注意到資料型態的晉升( Promotion )轉型( Casting )問題。

算術運算時資料型態的晉升

當您直接在程式中寫下 10 / 3 這樣的運算時,由於編譯器會配給 int 型態的空間給數值 10 與 3,當兩者進行除法運算時,最後的結果仍是 int 型態的數值,所以浮點數的資料無法容納,因而結果會只顯示 3。

在第四章節中曾經談到過型態晉升的議題,晉升是編譯器將較小的資料型態升級為較大的資料型態,使得它可以支援特定範圍的數值或運算,當運算式中有資料型態較大的數值時,所有其它的數值都會自動提升為符合該資料型態,接著再進行運算,如此才足以容納運算的結果。

所以當您進行 10.0 / 3 的運算時,int 型態的數值 3 也會轉換為浮點數 3.0 再進行運算,結果才會顯示浮點數的運算結果。

圖5-4、算術運算時的自動型態晉升

型態的晉升並不只發生於除法,只要運算式中有某個資料型態較大,所有的資料都會晉升為該型態。

算術運算時資料型態的轉型

您宣告了兩個 int 型態的變數並進行運算,並在某個時間點將兩個進行相除,根據之前的討論,兩個 int 型態的數值相除,結果只會顯示 int 型態的運算結果,然而您想得到的是浮點數的運算結果時該如何作?

您可以將某個數值進行轉型,「手動」將之晉升為較大的資料型態,方法是使用括號()並指定轉型的目標型態關鍵字,例如:

System.out.println((double) 10 / 3);
                            

將運算式中的某個數值提升為較大的資料型態之後,進行運算時,所有的資料就會以該資料型態的大小進行運算,以上例來說,運算的結果就會 3.3333...,而不只是 3。

圖5-5、算術運算的注意事項