Java SE 6 技術手冊

3.3 資料、運算

電腦的原文是「Computer」,電腦的基本作用其實就是運算,要運算就要給它資料,要告訴電腦您給了它哪些資料?是整數還是小數?是文字還是字元?這就是程式語言術語中所謂的「指定變數與資料型態」!在給定了資料之後,接著您就可以叫電腦開始進行各種算術、邏輯、比較、遞增、遞減等等的運算。

3.3.1 資料型態

程式在執行的過程中,需要運算許多的資訊,也需要儲存許多的資訊,這些資訊可能是由使用者輸入、從檔案中取得,甚至是由網路上得到,在程式運行的過程中,這些資訊透過「變數」(Variable)加以儲存,以便程式隨時取用。

一個變數代表一個記憶體空間,資料就是儲存在這個空間中,使用變數名稱來取得資料相信會比使用記憶體位置來得方便;然而由於資料在儲存時所需要的容量各不相同,不同的資料必須要配給不同大小的記憶體空間來儲存,在 Java 中對不同的資料區分有幾種不同的「資料型態」(Data type)。

在 Java 中基本的資料型態(Primitive type)主要區分為「整數」、「位元」、「浮點數」、「字元」與「布林數」,而這幾種還可以細分,如下所示:

  • 整數

    只儲存整數數值,可細分為短整數(short)(佔 2 個位元組)、整數(int)(佔4個位元組)與長整數(long)(佔 8 個位元組),長整數所佔的記憶體比整數來得多,可表示的數值範圍也就較大,同樣的,整數(int)可表示的整數數值範圍也比短整數來得大。

  • 位元

    Java 提供有位元(byte)資料型態,可專門儲存位元資料,例如影像的位元資料,一個位元資料型態佔一個位元組,而必要的話,byte 資料型態也可以用於儲存整數數值。

  • 浮點數

    主要用來儲存小數數值,也可以用來儲存範圍更大的整數,可分為浮點數(float)(佔 4 個位元組)與倍精度浮點數(double)(佔 8 個位元組),倍精度浮點數所使用的記憶體空間比浮點數來得多,可表示的數值範圍與精確度也比較大。

  • 字元

    用來儲存字元,Java 的字元採 Unicode 編碼,其中前 128 個字元編碼與 ASCII 編碼相容;每個字元資料型態佔兩個位元組,可儲存的字元範圍由 '\u0000'到'\uFFFF',由於 Java 的字元採用 Unicode 編碼,一個中文字與一個英文字母在 Java 中同樣都是用一個字元來表示。

  • 布林數

    佔記憶體 2 個位元組,可儲存 true 與 false 兩個數值,分別表示邏輯的「真」與「假」。 因為每種資料型態所佔有的記憶體大小不同,因而可以儲存的數值範圍也就不同,例如整數(int)的記憶體空間是 4 個位元組,所以它可以儲存的整數範圍為 -2147483648 至 2147483647,如果儲存值超出這個範圍的話稱之為「溢值」(Overflow),會造成程式不可預期的結果,您可以使用範例 3.12 來獲得數值的儲存範圍。

範例 3.12 DataRange.java

public class DataRange { 
    public static void main(String[] args) { 
        System.out.printf("short \t數值範圍:%d ~ %d\n", 
                             Short.MAX_VALUE, Short.MIN_VALUE); 
        System.out.printf("int \t數值範圍:%d ~ %d\n", 
                             Integer.MAX_VALUE, Integer.MIN_VALUE); 
        System.out.printf("long \t數值範圍:%d ~ %d\n",
                             Long.MAX_VALUE, Long.MIN_VALUE); 
        System.out.printf("byte \t數值範圍:%d ~ %d\n", 
                             Byte.MAX_VALUE, Byte.MIN_VALUE); 
        System.out.printf("float \t數值範圍:%e ~ %e\n", 
                             Float.MAX_VALUE, Float.MIN_VALUE); 
        System.out.printf("double \t數值範圍:%e ~ %e\n", 
                             Double.MAX_VALUE, Double.MIN_VALUE); 
    } 
}

其中 Byte、Integer、Long、Float、Double 都是 java.lang 套件下的類別名稱,而 MAX_VALUE 與 MIN_VALUE 則是各類別中所定義的靜態常數成員,分別表示該資料型態可儲存的數值最大與最小範圍,'%e' 表示用科學記號顯示,範例的執行結果如下所示:

short    數值範圍:32767 ~ -32768
int    數值範圍:2147483647 ~ -2147483648
long    數值範圍:9223372036854775807 ~ -9223372036854775808
byte    數值範圍:127 ~ -128
float    數值範圍:3.402823e+38 ~ 1.401298e-45
double    數值範圍:1.797693e+308 ~ 4.900000e-324

其中浮點數所取得是正數的最大與最小範圍,加上負號即為負數可表示的最大與最小範圍。

3.3.2 變數、常數

資料是儲存在記憶體中的一塊空間中,為了取得資料,您必須知道這塊記憶體空間的位置,然而若使用記憶體位址編號的話相當的不方便,所以使用一個明確的名稱,變數(Variable)是一個資料儲存空間的表示,您將資料指定給變數,就是將資料儲存至對應的記憶體空間,您呼叫變數時,就是將對應的記憶體空間的資料取出供您使用。

在 Java 中要使用變數,必須先宣告變數名稱與資料型態,例如:

int age;         // 宣告一個整數變數
double scope;   // 宣告一個倍精度浮點數變數 

就如上面所舉的例子,您使用 int、float、double、char 等關鍵字(Keyword)來宣告變數名稱並指定其資料型態,變數在命名時有一些規則,它不可以使用數字作為開頭,也不可以使用一些特殊字元,像是 *&^% 之類的字元,而變數名稱不可以與 Java 內定的關鍵字同名,例如 int、float、class 等。

變數的命名有幾個風格,主要以清楚易懂為主,初學者為了方便,常使用一些簡單的字母來作為變數名稱,這會造成日後程式維護的困難,命名變數時發生同名的情況也會增加。在過去曾流行過匈牙利命名法,也就是在變數名稱前加上變數的資料型態名稱縮寫,例如 intNum 用來表示這個變數是int整數資料型態,fltNum 表示一個 float 資料型態,然而隨著現在程式的發展規模越來越大,這種命名方式已經不被鼓勵。

現在比較鼓勵用清楚的名稱來表明變數的作用,通常會以小寫字母作為開始,並在每個單字開始時第一個字母使用大寫,例如:

int ageOfStudent;
int ageOfTeacher;

像這樣的名稱可以讓人一眼就看出這個變數的作用,在 Java 程式設計領域中是比較常看到的變數命名方式。變數名稱可以使用底線作為開始,通常使用底線作為開始的變數名稱,表示它是私用的(Private),只在程式的某個範圍使用,外界並不需要知道有這個變數的存在,通常這樣的變數名稱常用於物件導向程式設計中類別的私有成員(Private member),這樣的命名方式偶而也會看到,一個宣告的例子如下:

double _window_center_x;
double _window_center_y; 

當您在Java中宣告一個變數,就會配置一塊記憶體空間給它,這塊空間中原先可能就有資料,也因此變數在宣告後的值是不可預期的,Java 對於安全性的要求極高,您不可以宣告變數後,而在未指定任何值給它之前就使用它,編譯器在編譯時會回報這個錯誤,例如若宣告變數 var 卻沒有指定值給它,則會顯示以下訊息:

variable var might not have been initialized

可以的話,儘量在變數宣告後初始其值,您可以使用「指定運算子」(Assignment operator)'='來指定變數的值,例如:

int ageOfStudent = 0;
double scoreOfStudent = 0.0;
char levelOfStudent = 'A'; 

上面這段程式在宣告變數的時候,同時指定變數的儲存值,而您也看到如何指定字元給字元變數,字元在指定時需使用引號 ' ' 來包括;在指定浮點數時,會習慣使用小數的方式來指定,如 0.0,在J ava 中寫下 0.0 這麼一個常數的話,其預設為 double 資料型態。

在宣告變數之後,就可以呼叫變數名稱來取得其所儲存的值,範例 3.13是個簡單的示範:

範例 3.13 VariableDemo.java

public class VariableDemo { 
    public static void main(String[] args) { 
        int ageOfStudent = 5;
        double scoreOfStudent = 80.0; 
        char levelOfStudent = 'B'; 

        System.out.println("年級\t 得分\t 等級"); 
        System.out.printf("%4d\t %4.1f\t %4c", 
            ageOfStudent, scoreOfStudent, levelOfStudent); 
    } 
}

以下為執行結果:

年級     得分    等級
   5     80.0      B

在 Java 中寫下一個數值,這個數就稱之為字面常數(Literal constant),它會存在記憶體的某個位置,您無法改變它的值;而在使用變數的時候,您也會使用一種叫「常數」的變數,嚴格來說它並不是常數,只不過在指定數值給這個變數之後,就不可再改變其值,有人為了區分其與常數的差別,還給了它一個奇怪的名稱:「常數變數」。

先不要管「常數變數」這個怪怪的名稱,其實它終究是個變數而已,您在宣告變數名稱的同時,加上 "final" 關鍵字來限定,只不過這個變數一但指定了值,就不可以再改變它的值,如果程式中有其它程式碼試圖改變這個變數,編譯器會先檢查出這個錯誤,例如:

final int maxNum = 10;
maxNum = 20;

這一段程式碼中的 maxNum 變數您使用了 "final" 關鍵字來限定,所以它在指定為 10 之後,就不可以再指定值給它,所以第二次的值指定會被編譯器指出錯誤:

cannot assign a value to final variable maxNum

使用 "final" 來限定的變數,目的通常就是不希望其它的程式碼來變動它的值,例如用於迴圈計數次數的指定(迴圈之後就會學到),或是像圓周率 PI 常數的指定。

3.3.3 算術運算

程式的目的簡單的說就是運算,除了運算還是運算,所以加減乘除這類的操作是少不得的,在 Java 中提供運算功能的就是運算子 (Operator),例如與算術相關的有加(+)、減(-)、乘(*)、除(/)這類的運算子,另外還有一個也很常用的餘除運算子(%),這類以數學運算為主的運算子稱之為「算術運算子」(Arithmetic operator)

算術運算子的使用基本上與您學過的加減乘除一樣,也是先乘除後加減,必要時加上括號表示運算的先後順序,例如這個程式碼會在文字模式下顯示 7:

System.out.println(1 + 2 * 3);

編譯器在讀取程式碼時是由左往右讀取的,而初學者往往會犯一個錯誤,例如 (1+2+3) / 4,由於在數學運算上習慣將分子寫在上面,而分母寫在下面的方式,使得初學者往往將之寫成了:

System.out.println(1+2+3 / 4);

這個程式事實上會是這樣運算的:1+2+(3/4);為了避免這樣的錯誤,在必要的時候為運算式加上括號才是最保險的,例如:

System.out.println((double)(1+2+3) / 4);

注意在上面的程式碼中使用了 double 限定型態轉換,如果不加上這個限定的話,程式的輸出會是 1 而不是 1.5,這是因為在這個 Java 程式中,1、2、3、4 這四個數值都是整數,當程式運算 (1+2+3) 後的結果還是整數型態,若此時除以整數 4,會自動去除小數點之後的數字再進行輸出,而您加上 double 限定,表示要 (1+2+3) 運算後的值轉換為 double 資料型態,如此再除以 4,小數點之後的數字才不會被去除。

同樣的,您看看這段程式會印出什麼結果?

int testNumber = 10;
System.out.println(testNumber / 3); 

答案不是 3.3333 而是 3,小數點之後的部份被自動消去了,這是因為您的 testNumber 是整數,而除數3也是整數,運算出來的程式被自動轉換為整數了,為了解決這個問題,您可以使用下面的方法:

int testNumber = 10;
System.out.println(testNumber / 3.0); 
System.out.println((double) testNumber / 3); 

上面這個程式片段示範了兩種解決方式:如果運算式中有一個浮點數,則程式就會先轉換使用浮點數來運算,這是第一段陳述句所使用的方式;第二個方式稱之為「限定型態轉換」,您使用 double 告訴程式先將 testNumber 的值轉換為 double,然後再進行除法運算,所以得到的結果會是正確的 3.3333;型態轉換的限定關鍵字就是宣告變數時所使用的 int、float 等關鍵字。

當您將精確度小的資料型態(例如int)指定給精確度大的資料型態之變數時(例如 double),這樣的指定在精確度並不會失去,所以這樣的指定是可行的,由於 Java 對於程式的安全性要求極高,型態轉換在某些情況一定要明確指定,就是在使用指定運算子 '=' 時,將精確度大的值指定給精確度小的變數時,由於在精確度上會有遺失,編譯器會認定這是一個錯誤,例如:

int testInteger = 0;
double testDouble = 3.14;
testInteger = testDouble;
System.out.println(testInteger); 

這段程式在編譯時會出現以下的錯誤訊息:

possible loss of precision
found   : double
required: int 
testInteger = testDouble
               ^ 
 1 error 

如果您確定要轉換數值為較小的精度,您必須明確加上轉換的限定字,編譯器才不會回報錯誤。

testInteger = (int) testDouble;

'%' 運算子是餘除運算子,它計算所得到的結果是除法後的餘數,例如 (10 % 3) 會得到餘數 1;一個使用 '%' 的例子是數字循環,假設有一個立方體要進行360度旋轉,每次要在角度上加 1,而 360 度後必須復歸為 0,然後重新計數,這時您可以這麼撰寫:

count = (count + 1) % 360;

3.3.4 比較、條件運算

數學上有比較的運算,像是大於、等於、小於等運算,Java 中也提供了這些運算子,這些運算子稱之為「比較運算子」(Comparison operator),它們有大於(>)、不小於(>=)、小於(<)、不大於(<=)、等於(==)以及不等於(!=)。

在 Java 中,比較的條件成立時以 true 表示,比較的條件不成立時以 false 表示,範例 3.14 示範了幾個比較運算的使用。

範例 3.14 ComparisonOperator.java

public class ComparisonOperator {
    public static void main(String[] args) {
        System.out.println("10 >  5 結果 " + (10 > 5)); 
        System.out.println("10 >= 5 結果 " + (10 >= 5)); 
        System.out.println("10 <  5 結果 " + (10 < 5)); 
        System.out.println("10 <= 5 結果 " + (10 <= 5)); 
        System.out.println("10 == 5 結果 " + (10 == 5)); 
        System.out.println("10 != 5 結果 " + (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,而不是比較運算 x 與 y 是否相等。

另一個使用 '==' 運算時要注意的是,對於物件來說,兩個物件參考之間使用 '==' 作比較時,是比較其名稱是否參考至同一物件,而不是比較其內容,在之後的章節介紹 Integer 等包裹(Wrapper)物件或字串時會再詳細介紹。

既然談到了條件式的問題,來介紹 Java 中的「條件運算子」(conditional operator),它的使用方式如下:

條件式 ? 成立傳回值 : 失敗傳回值

條件運算子的傳回值依條件式的結果而定,如果條件式的結果為 true,則傳回冒號 ':' 前的值,若為 false,則傳回冒號後的值,範例 3.15 可以作個簡單的示範:

範例3.15 ConditionalOperator.java

import java.util.Scanner;

public class ConditionalOperator {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("請輸入學生分數: "); 
        int scoreOfStudent = scanner.nextInt(); 
        System.out.println("該生是否及格? " + 
                     (scoreOfStudent >= 60 ? '是' : '否'));
    }
}

這個程式會依您所輸入的分數來判斷學生成績是否不小於 60 分,以決定其是否及格,如果是則傳回字元 '是' ,否則傳回字元 '否',執行結果如下:

請輸入學生分數: 88
該生是否及格? 是

條件運算子(?:)使用得當的話可以少寫幾句程式碼,例如範例 3.16 可以判斷使用者輸入是否為奇數。

範例3.16 OddDecider.java

import java.util.Scanner;

public class OddDecider {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("請輸入數字: "); 
        int number = scanner.nextInt(); 
        System.out.println("是否為奇數? " + 
                      (number%2 != 0 ? '是' : '否')); 
    }
}

當您輸入的數為奇數時,就不能被2整除,所以餘數一定不是 0,在條件式中判斷為 true,因而傳回字元 '是',若數值為偶數,則 2 整除,所以餘數為 0,在條件式中判斷為 false,所以傳回字元 '否',一個執行的例子如下:

請輸入數字: 87
是否為奇數? 是

3.3.5 邏輯、位元運算

大於、小於的運算會了,但如果想要同時進行兩個以上的條件判斷呢?例如分數大於 80 「且」小於 90 的判斷。在邏輯上有 所謂的「且」(And)、「或」(Or)與「反」(Inverse),在 Java 中也提供這幾個基本邏輯運算所需的「邏輯運算子」(Logical operator),分別為「且」(&&)、「或」(||)及「反相」(!)三個運算子。 來看看範例 3.17 會輸出什麼?

範例3.17 LogicalOperator.java

public class LogicalOperator {
    public static void main(String[] args) {
        int 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 的「相反」為真。

接下來看看「位元運算子」(Bitwise operator),在數位設計上有 AND、OR、NOT、XOR 與補數等運算,在 Java 中提供這些運算的就是位元運算子,它們的對應分別是&(AND)、|(OR)、^(XOR)與~(補數)。

如果您不會基本的位元運算,可以從範例3.18中瞭解各個位元運算的結果。

範例3.18 BitwiseOperator.java

public class BitwiseOperator { 
    public static void main(String[] args) { 
        System.out.println("AND運算:"); 
        System.out.println("0 AND 0\t\t" + (0 & 0)); 
        System.out.println("0 AND 1\t\t" + (0 & 1)); 
        System.out.println("1 AND 0\t\t" + (1 & 0)); 
        System.out.println("1 AND 1\t\t" + (1 & 1)); 

        System.out.println("\nOR運算:"); 
        System.out.println("0 OR 0\t\t" + (0 | 0)); 
        System.out.println("0 OR 1\t\t" + (0 | 1)); 
        System.out.println("1 OR 0\t\t" + (1 | 0)); 
        System.out.println("1 OR 1\t\t" + (1 | 1)); 

        System.out.println("\nXOR運算:"); 
        System.out.println("0 XOR 0\t\t" + (0 ^ 0)); 
        System.out.println("0 XOR 1\t\t" + (0 ^ 1)); 
        System.out.println("1 XOR 0\t\t" + (1 ^ 0)); 
        System.out.println("1 XOR 1\t\t" + (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

Java 中的位元運算是逐位元運算的,例如 10010001 與 01000001 作 AND 運算,是一個一個位元對應運算,答案就是 00000001;而補數運算是將所有的位元 0 變 1,1 變 0,例如 00000001 經補數運算就會變為 11111110,例如下面這個程式所示:

byte number = 0; 
System.out.println((int)(~number));

這個程式會在主控台顯示 -1,因為 byte 佔記憶體一個位元組,它儲存的 0 在記憶體中是 00000000,經補數運算就變成 11111111,這個數在電腦中用整數表示則是 -1。

要注意的是,邏輯運算子與位元運算子也是很常被混淆的,像是'&&'與'&','||'與 '|',初學時可得多注意。

位元運算對初學者來說的確較不常用,但如果用的恰當的話,可以增進不少程式效率,例如範例 3.19 可以判斷使用者的輸入是否為奇數:

範例3.19 OddDecider2.java

import java.util.Scanner;

public class OddDecider2 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("請輸入數字: "); 
        int number = scanner.nextInt(); 
        System.out.println("是否為奇數? " + 
                  ((number&1) != 0 ? '是' : '否')); 
    }
}

執行結果:

請輸入數字: 66
是否為奇數? 否

範例 3.19 得以運作的原理是,奇數的數值若以二進位來表示,其最右邊的位元必為 1,而偶數最右邊的位元必為 0,所以您使用1來與輸入的值作 AND 運算,由於 1 除了最右邊的位元為 1 之外,其它位元都會是 0,與輸入數值 AND 運算的結果,只會留下最右邊位元為 0 或為 1 的結果,其它部份都被 0 AND 運算遮掉了,這就是所謂「位元遮罩」,例如 4 與 1 作 AND 運算的結果會是 0,所以判斷為偶數:

整數4:00000100
整數1:00000001
AND 運算後:00000000

而 3 與 1 作 AND 運算的結果是 1,所以判斷為奇數:

整數3:00000011
整數1:00000001
AND運算後:00000001

XOR 的運算較不常見,範例 3.20 舉個簡單的 XOR 字元加密例子。

範例3.20 XorCode.java

public class XorCode { 
    public static void main(String[] args) { 
        char ch = 'A'; 
        System.out.println("編碼前:" + ch); 

        ch = (char)(ch^7); 
        System.out.println("編碼後:" + ch); 

        ch = (char)(ch^7); 
        System.out.println("解碼:" + ch); 
    } 
}

0x7 是 Java 中整數的 16 進位寫法,其實就是 10 進位的 7,將位元與 1 作 XOR 的作用其實就是位元反轉,0x7 的最右邊三個位元為 1,所以其實就是反轉 ch 變數的最後兩個位元,如下所示:

ASCII 中的 'A' 字元編碼為 65:01000001 
整數 7:00000111
XOR 運算後:01000110

01000110 就是整數 70,對應 ASCII 中的字元 'F' 之編碼,所以用字元方式顯示時會顯示 'F' 字元,同樣的,這個簡單的 XOR 字元加密,要解密也只要再進行相同的位元反轉就可以了,看看範例 3.20 的執行結果:

編碼前:A
編碼後:F
解碼:A

良葛格的話匣子 要注意的是,我雖然在說明時都只寫下 8 個位元的值來說明,這只是為了解說方便而已。實際的位元長度在運算時,需依資料型態所佔的記憶體長度而定,例如在使用 int 型態的 0 作運算時,要考慮的是 32 個位元長度,而不是只有 8 個位元,因為 int 佔有 4 個位元組,也就是實際上是 00000000 00000000 00000000 00000000。

在位元運算上,Java 還有左移(<<)與右移(>>)兩個運算子,左移運算子會將所有的位元往左移指定的位數,左邊被擠出去的位元會被丟棄,而右邊會補上0;右移運算則是相反,會將所有的位元往右移指定的位數,右邊被擠出去的位元會被丟棄,至於最左邊補上原來的位元,如果左邊原來是 0 就補 0,1 就補 1。另外還有 >>> 運算子,這個運算子在右移後,一定在最左邊補上 0。

範例 3.21 使用左移運算來作簡單的2次方運算示範。

範例3.21 ShiftOperator.java

public class ShiftOperator { 
    public static void main(String[] args) { 
        int number = 1; 
        System.out.println( "2的0次: " + number); 

        number = number << 1; 
        System.out.println("2的1次: " +  number); 

        number = number << 1; 
        System.out.println("2的2次: " + number); 

        number = number << 1; 
        System.out.println("2的3次:" + number); 
    } 
}

執行結果:

2的0次: 1 
2的1次: 2 
2的2次: 4 
2的3次:8 

實際來左移看看就知道為何可以如此作次方運算了:

00000001 -> 1 
00000010 -> 2 
00000100 -> 4 
00001000 -> 8

良葛格的話匣子 位元運算對於沒有學過數位邏輯的初學者來說,會比較難一些,基本上除了像是資訊工程、電機工程相關領域的開發人員會比較常使用位元運算之外,大部份的開發人員可能不常使用位元運算,如果您的專長領域比較不需要使用位元運算,則基本上先瞭解有位元運算這個東西就可以了,不必在這個部份太過鑽研。

3.3.6 遞增、遞減運算

遞增(Increment)、遞減(Decrement)與指定(Assignment)運算子,老實說常成為初學者的一個惡夢,因為有些程式中若寫得精簡,這幾個運算子容易讓初學者搞不清楚程式的真正運算結果是什麼;事實上,使用這幾種運算子的目的除了使讓程式看來比較簡潔之外,還可以稍微增加一些程式執行的效率。 在程式中對變數遞增1或遞減1是很常見的運算,例如:

int i = 0; 
i = i + 1; 
System.out.println(i); 
i = i - 1; 
System.out.println(i);

這段程式會分別顯示出 1 與 0 兩個數,您可以使用遞增、遞減運算子來撰寫程式:

範例 3.22 IncrementDecrement.java

public class IncrementDecrement {
    public static void main(String[] args) {
        int i = 0;
        System.out.println(++i); 
        System.out.println(--i);
    }
}

其中寫在變數 i 之前的 ++ 與 -- 就是「遞增運算子」(Increment operator)與「遞減運算子」(Decrement operator),當它們撰寫在變數之前時,其作用就相當於將變數遞增 1 與遞減 1:

++i;    // 相當於 i = i + 1; 
--i;    // 相當於 i = i - 1; 

您可以將遞增或遞減運算子撰寫在變數之前或變數之後,但其實兩種寫法是有差別的,將遞增(遞減)運算子撰寫在變數前時,表示先將變數的值加(減)1,然後再傳回變數的值,將遞增(遞減)運算子撰寫在變數之後,表示先傳回變數值,然後再對變數加(減)1,例如:

範例 3.23 IncrementDecrement2.java

public class IncrementDecrement2 {
    public static void main(String[] args) {
        int i = 0; 
        int 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,再看看範例 3.24。

範例 3.24 IncrementDecrement3.java

public class IncrementDecrement3 {
    public static void main(String[] args) {
        int i = 0; 
        int 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。

接下來看「指定運算子」(Assignment operator),到目前為止您只看過一個指定運算子,也就是 '=' 這個運算子,事實上指定運算子還有以下的幾個:

表 3.3 指定運算子

指定運算子 範例 結果
+= 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

每個指定運算子的作用如上所示,但老實說若不是常寫程式的老手,當遇到這些指定運算子時,有時可能會楞一下,因為不常用的話,這些語法並不是那麼的直覺。

使用 ++、- - 或指定運算子,由於程式可以直接在變數的記憶體空間中運算,而不用取出變數值、運算再將數值存回變數的記憶體空間,所以可以增加運算的效率,但以現在電腦的運算速度來看,這一點的效率可能有些微不足道,除非您這類的運算相當的頻繁,否則是看不出這點效率所帶來的改善,就現在程式撰寫的規模來看,程式的易懂易讀易維護會比效率來的重要,可以的話儘量將程式寫的詳細一些會比較好,千萬不要為了賣弄語法而濫用這些運算子。

就單一個陳述而言,使用 ++、- - 或指定運算子是還算可以理解,但與其它陳述結合時可就得考慮一下,例如:

int i = 5; 
arr[--i %= 10] = 10; 

像這樣的式子,要想知道變數 i 是多少,以及陣列的指定索引位置在哪可就得想一下了(有興趣算一下的話,i 最後會是 4,而陣列的指定索引也是 4),總之,如何使用與何時使用,自己得拿捏著點。

良葛格的話匣子 在撰寫程式時,可以在運算子的左右或是逗號 ',' 之後適當的使用一些空白,讓程式看來不那麼擁擠,不僅程式看來比較美觀,閱讀起來也比較容易,比較一下就可以體會:

int i=0; 
int number=0; 
number=i++; 
number=i--;

下面的寫法會比較好讀一些:

int i = 0; 
int number = 0; 
number = i++; 
number = i--;