第 3 章 語法入門

程式語言(Programming Language)本質上就是一個語言,語言的目的在於讓您與特定對象進行溝通,只不過程式語言的溝通對象是電腦。現在您學習的是 Java 語言,用 Java 寫程式的目的就是在告訴電腦,您希望它為您作哪些事,Java 既然是語言,就有其規定的語法,這個章節就是在學習基本的 Java 語法。

我建議初學者從文字模式學習如何操作程式,您在這個章節中將會學到:如何告訴電腦在命令模式下顯示訊息?什麼時候該等待您輸入資料(即 Console 互動)?程式中您要使用的資料有哪些(即資料型態)?您要用這些資料進行什麼樣的運算(即各種運算子的使用)?在某些情況滿足時下一步要作什麼?不滿足某些條件時又該作些什麼(即流程控制)?


3.1 第一個 Java 程式

先來解釋一下第 2 章中您所完成的「第一個 Java 程式」,如果您學過 C 語言,對於 3.1.2「給 C 使用者的第一個 Java 程式」可能會覺得比較親切,因為介紹了類似 C 語言中 printf() 函式的功能,另外別忘了在寫程式時加入一些註解文字,這在閱讀程式碼的時候會很有幫助。

3.1.1 解釋第一個 Java 程式

要對新手解釋第一個 Java 程式事實上並不簡單,因為一個最簡單的 Java 程式就會涉及檔案管理、類別 (Class)、主程式、命令列引數(Command line argument)等觀念,我很想對您說,反正一個基本的 Java 程式就這麼寫就對了,因為要一下子接受這麼多觀念並不容易。總之,如果現階段您無法瞭解,就請當它是 Java 語言的文法規範。

先重新列出您的第一個 Java 程式以方便說明。

範例 3.1 HelloJava.java

  1. public class HelloJava {
  2. public static void main(String[] args) {
  3. System.out.println("嗨!我的第一個Java程式!");
  4. }
  5. }
  • 定義類別(Class)

    撰寫 Java 程式通常都是由定義「類別」開始,”class” 是 Java 用來定義類別的關鍵字,範例中類別的名稱是 HelloJava,這與您編輯的檔案(HelloJava.java)主檔名必須相同,HelloJava 類別使用關鍵字 “public” 宣告,在編寫 Java 程式時,一個檔案中可撰寫數個類別,但是只能有一個公開(public)類別,而且檔案主檔名必須與這個公開類別的名稱相同,在定義類別名稱時,建議將類別首字母大寫,並在類別名稱上表明類別的作用。

  • 定義區塊(Block)

    在 Java 程式使用大括號 ‘{‘ 與 ‘}’來定義區塊,大括號兩兩成對,目的在區別定義的作用範圍,例如您的程式中,HelloJava 類別的區塊包括了 main(),而 main() 的區塊包括了一句顯示訊息的程式碼。

  • 定義 main() 方法(Method)

    main() 是 Java 程式的「進入點」(Entry point),程式的執行是由進入點開始的,類別中的方法是類別的成員(Member),main() 方法一定是個 “public” 成員(member),這樣它才可以執行環境被呼叫;main() 方法不需要產生物件(Object)就能被執行,所以它必須是個 “static” 成員,”public” 與 “static” 的觀念都是 Java 物件導向(Object-oriented)程式上的觀念,之後專門討論類別與物件時會再介紹,這邊先不用理解 “public” 與 “static” 的真正意涵。

    main() 之前的「void」表示 main() 執行結束後不傳回任何值,Java 程式的 main() 方法不需傳回任何值,所以一律宣告 void;main() 括號間的 String[] args 可以在執行程式時取得使用者指定的命令列引數(Command line argument),目前雖然您用不到,但仍要撰寫它,這是規定。

  • 撰寫陳述(Statement)

    來看 main() 當中的一行陳述:

    1. System.out.println("嗨!我的第一個Java程式!");

    陳述是程式語言中的一行指令,簡單的話就是程式語言的「一句話」。注意每一句陳述的結束要用分號 ‘ ; ‘ ,在上面的陳述中,您使用了 java.lang 套件(package)下的 System 類別的公開(public)成員 out 物件,out 是一個 PrintStream 物件,您使用了 PrintStream 所提供的 println() 方法,將當中指定的字串(String) “嗨!我的第一個Java程式!” 輸出至文字模式上。

    注意在 Java 中字串要使用 “” 包括,println() 表示輸出字串後自動換行,如果使用 print() 的話,則輸出字串後程式並不會自動斷行。

一個最基本的 Java 程式完成了,接下來要編譯程式了,關於編譯在第 2 章中有介紹過,這邊再作個提示,編譯時是使用 javac 公用程式,如下所示:

  1. javac HelloJava.java

編譯完成後,預設在同一個目錄下會產生一個 HelloJava.class 的位元碼(bytecodes)檔案,在執行時期可以由執行環境轉換為平台可執行的機器碼,執行程式時是使用 java 工具程式,如下所示:

  1. java HelloJava

注意最後並沒有加上 *.class 的副檔名,您只要提供類別名稱就可以了,程式畫面會顯示如下:

  1. 嗨!我的第一個Java程式!

良葛格的話匣子 定義區塊的風格因人而異,有的設計人員習慣先換行再定義區塊,例如:


  1. public class HelloJava
    {
    public static void main(String[] args)
    {
    System.out.println(“嗨!我的第一個Java程式!”);
    }
    }

這麼作的好處是可以很快的找到兩兩成對的大話號,區塊對應清楚。找一個您喜歡的風格撰寫,以清晰易讀為原則就可以了。

3.1.2 給 C 使用者的第一個 Java 程式

學過 C 語言的設計人員總是對 printf() 方法的功能總是難以忘懷,他們在學 Java 語言時常不免感概:「是的!是 printf(),他們忘了把 printf() 放進去了….」。

在某些時候,printf() 方法中字串上可以指定引數來進行輸出的功能確實令人難以割捨,幸好,Java 在 J2SE 5.0 這個版本中,總算給了 C 使用者類似 printf() 的功能了,如果您是學過 C 的使用者,下面這第一個 Java 程式或許會讓您高興一些:

範例 3.2 HelloJavaForC.java

  1. public class HelloJavaForC {
  2. public static void main(String[] args) {
  3. System.out.printf("%s! 這是您的第一個Java程式!\n",
  4. "C語言Fan");
  5. }
  6. }

這次使用的是 out 物件的 printf() 方法,’%s’ 對應於第一個字串 “C語言Fan”,程式的輸出會是如下:

  1. C語言Fan 這是您的第一個Java程式!

‘\n’ 對 C 程式設計人員並不陌生,它是換行字元,您也可以如下使用 println() 進行換行。

範例 3.3 SecondJavaForC.java

  1. public class SecondJavaForC {
  2. public static void main(String[] args) {
  3. System.out.printf("%s! 這是您的第二個Java程式!",
  4. "C語言Fan").println();
  5. }
  6. }

程式的輸出會是如下:

  1. C語言Fan 這是您的第二個Java程式!

在 printf() 要指定數字對應的話,可以使用 ‘%d’,例如:

範例 3.4 ThirdJavaForC.java

  1. public class ThirdJavaForC {
  2. public static void main(String[] args) {
  3. System.out.printf("%s! 這是您的第 %d 個Java程式!\n",
  4. "C語言Fan", 3);
  5. }
  6. }

‘%s’ 對應至 “C語言Fan”,而 ‘%d’ 對應至數字 3,所以程式的輸出會是如下:

  1. C語言Fan 這是您的第 3 Java程式!

這邊再作一次提醒,printf() 方法是在 J2SE 5.0 之後才有的功能,1.4 或更早版本的 JDK 並沒有辦法編譯範例 3.2、3.3 與 3.4。

良葛格的話匣子 如果您安裝的是 JDK6,即使您沒有使用到 JDK6 的新功能,所編譯出來的 class 檔預設也是無法在更早版本的 JRE 上運行的,因為 JDK6 編譯出來的 class 檔版號跟較早版本的JVM所接受的版號並不相同。

在不使用 JDK6 的新功能下,如果您希望編譯出來的 class 可以在 1.5 版本或之前版本上的環境上運行,則編譯時必須指定 -source 與 -target 引數,-source 指定原始碼中可以使用的版本功能,-target 指定編譯出來的位元碼適用的虛擬機器版本,例如您想要編譯出來的檔案在 1.5 版本環境上運行的話,可以如下編譯程式:

  1. javac -source 1.5 -target 1.5 HelloJava.java

3.1.3 為程式加入註解

在撰寫程式的同時,您可以為程式碼加上一些註解(Comment),說明或記錄您的程式中一些要注意的事項,Java 語言是您用來與電腦溝通的語言,而註解的作用則是與開發人員溝通。

原始碼檔案中被標註為註解的文字,編譯器不會去處理它,所以註解文字中撰寫任何的東西,對編譯出來的程式不會有任何影響。在 Java 中可以用兩種方式為程式加上註解,以範例 3.4 為例,您可以為它加上一些註解文字,例如:

  1. /* 作者:良葛格
  2. * 功能:示範printf()方法
  3. * 日期:2005/4/30
  4. */
  5. public class ThirdJavaForC {
  6. public static void main(String[] args) {
  7. // printf() 是J2SE 5.0的新功能,必須安裝JDK 5.0才能編譯
  8. System.out.printf("%s! 這是您的第 %d 個Java程式!\n",
  9. "C語言Fan", 3);
  10. }
  11. }

‘/‘ 與 ‘/‘ 用來包括跨行的註解文字,通常開發人員為了讓註解文字看來比較整齊清晰,中間還會使用一些 ‘‘ 來排版,只要記得使用多行註解時,是以 ‘/‘ 開始註解文字,以 ‘*/‘ 結束註解文字,所以您不能用巢狀方式來撰寫多行註解,例如下面的方式是不對的:

  1. /* 註解文字1……bla…bla
  2. /*
  3. 註解文字2……bla…bla
  4. */
  5. */

編譯器在處理前面的撰寫方式時,會以為倒數第二個 ‘/‘ 就是註解結束的時候,因而對最後一個 ‘/‘ 就會認為是錯誤的符號,這時就會出現編譯錯誤的訊息。

‘//‘ 則可以用來撰寫單行註解,在 ‘//‘ 之後的該行文字都被視為註解文字,多行註解可以包括單行註解,例如:

  1. /* 註解文字1……bla…bla
  2. // 註解文字2……bla…bla
  3. */

註解的撰寫時機與內容並沒什麼特別的規定,以清晰易懂為主,註解的目的在於說明程式,是給開發人員看的,您可以使用註解在程式中寫下重要事項,日後作為備忘或是方便其他開發人員瞭解您的程式內容。

註解的另一個作用則是用來暫時註銷某些陳述句,當您覺得程式中某些陳述有問題,可以使用註解標示起來,如此編譯器就不會去處理,例如同樣以範例 3.4 為例:

  1. public class ThirdJavaForC {
  2. public static void main(String[] args) {
  3. // System.out.println("C語言Fan!這是您的第3個Java程式!");
  4. System.out.printf("%s! 這是您的第 %d 個Java程式!\n",
  5. "C語言Fan", 3);
  6. }
  7. }

在’//‘之後的內容被視為註解文字,編譯器不會去處理,所以執行時不會執行該行陳述,如果日後要重新恢復那些陳述句的功能,只要將註解符號消去就可以了。

3.2 在文字模式下與程式互動

從互動中學習,是我最喜愛的學習方式,我建議學習 Java 的第一步,要先能看到您的程式執行結果,要可以對程式輸入一些資料,作一些傻瓜式的互動。

3.2.1 使用 Scanner 取得輸入

在文字模式下要輸入資料至程式中時,您可以使用標準輸入串流物件 System.in,然而實際上很少直接使用它,因為 System.in 物件所提供的 read() 方法,是從輸入串流取得一個位元組的資料,並傳回該位元組的整數值,但通常您要取得的使用者輸入會是一個字串,或是一組數字,所以 System.in 物件的 read() 方法一次只讀入一個位元組資料的方式並不適用。

在 J2SE 5.0 中,您可以使用 java.util.Scanner 類別取得使用者的輸入,java.util 指的是套件(package)層級,java.util.Scanner 表示 Scanner 這個類別是位於 java/util 這樣的階層之下,現階段您可以將這個階層想像為類似檔案管理的目錄階層。
直接使用實例來看看如何使用 Scanner 取得使用者的輸入字串:

範例 3.5 ScannerDemo.java

  1. import java.util.Scanner;
  2. public class ScannerDemo {
  3. public static void main(String[] args) {
  4. Scanner scanner = new Scanner(System.in);
  5. System.out.print("請輸入您的名字:");
  6. System.out.printf("哈囉! %s!\n", scanner.next());
  7. }
  8. }

先看看執行結果再來解釋程式的內容:

  1. 請輸入您的名字:良葛格
  2. 哈囉! 良葛格!

java.util 套件是 Java SE 的標準套件,您使用 import 是在告訴編譯器,您將使用 java.util下 的 Scanner 類別。

new 關鍵字表示您要新增一個 Scanner 物件,在新增一個 Scanner 物件時需要一個 System.in 物件,因為實際上還是 System.in 在取得使用者的輸入,您可以將 Scanner 看作是 System.in 物件的支援者,System.in 取得使用者輸入之後,交給 Scanner 作一些處理(實際上,這是 Decorator 模式的一個應用,請見這一章後的索引)。

不以 Java 術語而以白話來說,您告訴執行環境給您一個叫作 Scanner 的工具,然後您可以使用這個工具的 next() 功能,來取得使用者的輸入字串,但要如何取得數字呢?您可以使用 Scanner 工具的 nextInt() 功能,例如:

範例 3.6 ScannerDemo2.java

  1. import java.util.Scanner;
  2. public class ScannerDemo2 {
  3. public static void main(String[] args) {
  4. Scanner scanner = new Scanner(System.in);
  5. System.out.print("請輸入一個數字: ");
  6. System.out.printf("您輸入了 %d !\n",
  7. scanner.nextInt());
  8. }
  9. }

nextInt() 會將試著取得的字串轉換為 int 型態的整數,關於資料型態(Data type)我待會就會介紹,先來看看執行結果:

  1. 請輸入一個數字: 100
  2. 您輸入了 100

依同樣的方式,您還可以使用 Scanner 的 nextFloat()、nextBoolean() 等方法來取得使用者的輸入,並轉換為正確的資料型態。

在 JDK6 之中,您還可以使用 System.console() 方法取得一個 java.io.Console 物件,利用它來取得使用者輸入非常的方便,關於 Console 的介紹,請見第 21 章關於 JDK6 新功能的介紹。

3.2.2 使用 BufferedReader 取得輸入

Scanner 取得輸入的依據是空白字元,舉凡按下空白鍵、tab 鍵或是 enter 鍵,Scanner 就會傳回下一個輸入,所以在某些時候並不適用,因為使用者可能輸入一個字串,中間會包括空白字元,而您希望取得完整的字串,如果您想要取得包括空白字元的輸入,比較簡單的方法是使用 java.io.BufferedReader 類別取得輸入,這個方法也是在 Java SE 1.4 或之前版本下取得使用者輸入的方式。

必須先說明的是,關於使用 BufferedReader 來取得輸入,在理解上要複雜的多,我在這邊先加以說明,但如果您現階段無法理解的話不用氣餒,因為使用方法是固定的,每次使用前先如法泡製就可以了,在第14章介紹完輸入輸出,您就會理解 BufferedReader 的運作方式。

BufferedReader 類別是 java.io 套件中所提供的一個類別,所以使用這個類別時必須使用 import 告訴編譯器這個類別位於 java.io 套件下。

使用 BufferedReader 物件的 readLine() 方法必須處理 java.io.IOException 例外(exception),例外處理機制是 Java 提供給程式設計人員捕捉程式中可能發生的錯誤所提供的機制,現階段您處理 IOException 的方法是在 main() 方法加上 throws IOException,這在第 10 章介紹例外處理時,我會再詳細介紹為何要這麼作。

BufferedReader 在建構時接受一個 java.io.Reader 物件,在讀取標準輸入串流時,您可以使用 java.io.InputStreamReader,它繼承(Inherit)了 Reader類別,您可以使用以下的方法來為標準輸入串流建立緩衝區物件:

  1. BufferedReader bufferedReader = new BufferedReader(
  2. new InputStreamReader(System.in));

BufferedReader bufferedReader 表示宣告一個型態為 BufferedReader 的參考名稱,而 new BufferedReader() 表示您以 BufferedReader 類別建構一個物件,new InputStreamReader(System.in) 表示接受一個 System.in 物件來建構一個 InputStreamReader 物件。

不用Java術語而用白話來解釋上一段的話,就是您增加一個 BufferedReader 工具,這個工具中還要加上一個 InputStreamReader 工具,而 InputStreamReader 工具中實際的核心是 System.in 工具,這三個工具組合在一起,就可以讓您進行文字輸入的讀取。

如果還是不理解的話沒關係,照著抄寫就可以了,實際看個例子瞭解如何使用 BufferedReader 來讀取輸入。

範例 3.7 BufferedReaderDemo.java

  1. import java.io.*;
  2. public class BufferedReaderDemo {
  3. public static void main(String[] args) throws IOException {
  4. BufferedReader bufferedReader =
  5. new BufferedReader(
  6. new InputStreamReader(System.in));
  7. System.out.print("請輸入一列文字,可包括空白: ");
  8. String text = bufferedReader.readLine();
  9. System.out.println("您輸入的文字: " + text);
  10. }
  11. }

由於這次您所使用到的 BufferedReader、InputStreamReader 與 IOException 等類別,都是位在 java.io 套件下,所以在程式的一開頭可以使用 import 與 * 號,告訴編譯器到 java.io 下找這些類別。

readLine() 方法會傳回使用者在按下 Enter 鍵之前的所有字元輸入,不包括最後按下的 Enter 返回字元,程式的執行範例如下所示:

  1. 請輸入一列文字,可包括空白: Have a nice day :)
  2. 您輸入的文字: Have a nice day :)

良葛格的話匣子 學習一個新的事物時,如果遇到一些觀念無法馬上理解,這可能是因為要理解觀念會需要其它觀念先建立起來,所以先暫時放下這個疑問也是一個學習方法,稱之為「闕疑」,在往後的學習過程中待必要的觀念學會後,目前的疑問自然也會解開。

但什麼該闕疑?什麼觀念又該先徹底理解?在這本書中我會根據經驗告訴您,在告訴您闕疑的同時,也會告訴您這些觀念實際上在哪個章節(或網路資料上)會詳細說明。

3.2.3 標準輸入輸出串流

在之前的範例程式中,您使用了 System 類別中的靜態物件 out,它提供標準輸出串流(Stream)輸出,會在程式開始執行之後自動開啟並準備接受指定的資料,它通常對應至顯示輸出(文字模式、終端機輸出),您可以將輸出重新導向至一個檔案,只要執行程式時使用’>’將輸出結果導向至指定的檔案,例如:

  1. java HelloJava > HelloJavaResult.txt

上面的執行會將原本文字模式下的顯示結果導向至 HelloJavaResult.txt,而不會在螢幕上顯示訊息,HelloJavaResult.txt 將會有執行的結果文字” 嗨!我的第一個Java程式!”。

除了標準輸出串流 out 之外,Java 程式在執行之後,還會開啟標準輸入串流 in 與標準錯誤輸出串流 err。標準輸入串流 in 也是 System 類別所提供的靜態物件,在程式開始之後它會自動開啟,對應至鍵盤或其它的輸入來源,準備接受使用者或其它來源的輸入,您可以使用 read() 方法來讀取輸入,不過通常很少直接使用,而會使用一個 Scanner 物件或 BufferedReader 來讀取輸入,方法我已經在前面的內容介紹過了。

標準錯誤輸出串流 err 也是在程式執行後自動開啟,它會將指定的字串輸出至顯示裝置或其它指定的裝置,與標準輸出串流 out 不同的是,err 會立即顯示指定的(錯誤)訊息給使用者知道,即使您指定程式將結果重新導向至檔案,err 輸出串流的訊息也不會被重新導向,而仍會顯示在指定的顯示裝置上,下面這個例子給您一個簡單的測試方式:

範例 3.8 ErrDemo.java

  1. public class ErrDemo {
  2. public static void main(String[] args) {
  3. System.out.println("使用out輸出訊息");
  4. System.err.println("使用err輸出訊息");
  5. }
  6. }

在編譯程式之後,請如下執行程式,您會發現輸出結果如下:

  1. java ErrDemo > ErrDemoResult.txt
  2. 使用err輸出訊息

開啟 ErrDemoResult.txt 之後,您會發現當中只有 “使用out輸出訊息” 的文字,而”使用 err 輸出訊息”的文字並沒有被導向至檔案中,而是直接顯示在文字模式中。

要重新導向輸出至指定的目的檔案是用 ‘>’,也可以使用 ‘>>’,後者除了重導標準輸出之外,還有附加(Append)的功能,也就是會把輸出的內容附加到目的檔案的後頭,如果目的檔案本來不存在就會先建立一個再進行輸出。

3.2.4 輸出格式控制

標準輸出通常是使用文字模式作為輸出,這邊我介紹幾個輸出格式控制技巧,在文字模式顯示時可以協助控制輸出的格式,首先介紹格式控制字元,先使用表格列出一些常用的控制字元:

表 3.1 常用格式控制字元

控制字元 作用
\\ 反斜線
\’ 單引號’
\” 雙引號”
\uxxxx 以 16 進位數指定 Unicode 字元輸出
\xxx 以 8 進位數指定 Unicode 字元輸出
\b 倒退一個字元
\f 換頁
\n 換行
\r 游標移至行首
\t 跳格(一個Tab鍵)

範例 3.9 告訴您如何指定 Unicode 字元編碼來輸出 “Hello” 這段文字。

範例 3.9 OutputUnicode.java

  1. public class OutputUnicode {
  2. public static void main(String[] args) {
  3. System.out.println("\u0048\u0065\u006C\u006C\u006F");
  4. }
  5. }

在輸出數值時,預設都會以十進位的方式來顯示數值,範例 3.10 告訴您如何使用 java.lang.Integer 所提供的各種 toXXX() 方法來顯示不同進位制之數值。

範例 3.10 NumberDemo.java

  1. public class NumberDemo {
  2. public static void main(String[] args) {
  3. // 十進位 19 轉成二進位 10011
  4. System.out.println(Integer.toBinaryString(19));
  5. // 十進位 19 轉成十六進位 13
  6. System.out.println(Integer.toHexString(19));
  7. // 十進位 19 轉成八進位 23
  8. System.out.println(Integer.toOctalString(19));
  9. }
  10. }
  11. `

若是使用 J2SE 5.0 或更高的版本,您可以使用 System.out.printf() 作簡單的輸出格式設定,例如:

範例 3.11 TigerNumberDemo.java

  1. public class TigerNumberDemo {
  2. public static void main(String[] args) {
  3. // 輸出 19 的十進位表示
  4. System.out.printf("%d%n", 19);
  5. // 輸出 19 的八進位表示
  6. System.out.printf("%o%n", 19);
  7. // 輸出 19 的十六進位表示
  8. System.out.printf("%x%n", 19);
  9. }
  10. }

‘%d’ 表示將指定的數值以十進位表示,’%o’ 是八進位表示,而 ‘%x’ 是十六進位表示,’%n’ 是指輸出平台特定的換行字元,如果是在 Windows 下實際上會置換為 ‘r\n’,如果是 Linux 下則會置換為 ‘\n’。

表3.2 簡單列出了一些常用的轉換符號。

表 3.2 常用格式轉換字元

格式字元 作用
%% 在字串中顯示 %
%d 以 10 進位整數方式輸出,提供的數必須是 Byte、Short、 Integer、Long 或 BigInteger
%f 將浮點數以 10 進位方式輸出,提供的數必須是 Float、Double 或 BigDecimal
%e, %E 將浮點數以 10 進位方式輸出,並使用科學記號,提供的數必須是 Float、 Double 或 BigDecimal
%a, %A 使用科學記號輸出浮點數,以 16 進位輸出整數部份,以 10 進位輸出指數部份,提供的數必須是 Float、Double、BigDecimal
%o 以 8 進位整數方式輸出,提供的數必須是 Byte、Short、 Integer、Long 或 BigInteger
%x, %X 將浮點數以 16 進位方式輸出,提供的數必須是 Byte、Short、 Integer、Long 或 BigInteger
%s, %S 將字串格式化輸出
%c, %C 以字元方式輸出,提供的數必須是 Byte、Short、Character 或 Integer
%b, %B 將 “true” 或 “false” 輸出(或 “TRUE”、”FALSE”,使用 %B)。另外,非 null 值輸出是 “true”,null 值輸出是 “false”
%t, %T 輸出日期/時間的前置,詳請看線上 API 文件

您可以在輸出浮點數時指定精度,例如以下這行的執行結果會輸出 “ example:19.23”:

  1. System.out.printf("example:%.2f%n", 19.234);

您也可以指定輸出時,至少要預留的字元寬度,例如:

  1. System.out.printf("example:%6.2f%n", 19.234);

由於預留了 6 個字元寬度,不足的部份要由空白字元補上,所以執行結果會輸出如下(19.23 只佔五個字元,所以補上一個空白在前端):

  1. example: 19.23

以上只是簡短的列出一些常用的輸出轉換符號,事實上,這些功能都是由 java.util.Formatter 所提供的,如果您需要更多關於輸出格式的控制,您可以看看線上 API 文件以查詢相關設定。

良葛格的話匣子 由於書的篇幅有限,我只在必要的時候列出一些常用的 API 文件表格說明,如果需要更多的說明,請直接查詢我所提供的文件索引,學會查詢線上文件也是學好 Java 的必備功夫喔!

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

  1. public class DataRange {
  2. public static void main(String[] args) {
  3. System.out.printf("short \t數值範圍:%d ~ %d\n",
  4. Short.MAX_VALUE, Short.MIN_VALUE);
  5. System.out.printf("int \t數值範圍:%d ~ %d\n",
  6. Integer.MAX_VALUE, Integer.MIN_VALUE);
  7. System.out.printf("long \t數值範圍:%d ~ %d\n",
  8. Long.MAX_VALUE, Long.MIN_VALUE);
  9. System.out.printf("byte \t數值範圍:%d ~ %d\n",
  10. Byte.MAX_VALUE, Byte.MIN_VALUE);
  11. System.out.printf("float \t數值範圍:%e ~ %e\n",
  12. Float.MAX_VALUE, Float.MIN_VALUE);
  13. System.out.printf("double \t數值範圍:%e ~ %e\n",
  14. Double.MAX_VALUE, Double.MIN_VALUE);
  15. }
  16. }

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

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

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

3.3.2 變數、常數

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

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

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

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

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

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

  1. int ageOfStudent;
  2. int ageOfTeacher;

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

  1. double _window_center_x;
  2. double _window_center_y;

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

  1. variable var might not have been initialized

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

  1. int ageOfStudent = 0;
  2. double scoreOfStudent = 0.0;
  3. char levelOfStudent = 'A';

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

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

範例 3.13 VariableDemo.java

  1. public class VariableDemo {
  2. public static void main(String[] args) {
  3. int ageOfStudent = 5;
  4. double scoreOfStudent = 80.0;
  5. char levelOfStudent = 'B';
  6. System.out.println("年級\t 得分\t 等級");
  7. System.out.printf("%4d\t %4.1f\t %4c",
  8. ageOfStudent, scoreOfStudent, levelOfStudent);
  9. }
  10. }

以下為執行結果:

  1. 年級 得分 等級
  2. 5 80.0 B

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

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

  1. final int maxNum = 10;
  2. maxNum = 20;

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

  1. cannot assign a value to final variable maxNum

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

3.3.3 算術運算

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

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

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

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

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

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

  1. 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,小數點之後的數字才不會被去除。

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

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

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

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

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

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

  1. int testInteger = 0;
  2. double testDouble = 3.14;
  3. testInteger = testDouble;
  4. System.out.println(testInteger);

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

  1. possible loss of precision
  2. found : double
  3. required: int
  4. testInteger = testDouble
  5. ^
  6. 1 error

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

  1. testInteger = (int) testDouble;

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

  1. count = (count + 1) % 360;

3.3.4 比較、條件運算

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

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

範例 3.14 ComparisonOperator.java

  1. public class ComparisonOperator {
  2. public static void main(String[] args) {
  3. System.out.println("10 > 5 結果 " + (10 > 5));
  4. System.out.println("10 >= 5 結果 " + (10 >= 5));
  5. System.out.println("10 < 5 結果 " + (10 < 5));
  6. System.out.println("10 <= 5 結果 " + (10 <= 5));
  7. System.out.println("10 == 5 結果 " + (10 == 5));
  8. System.out.println("10 != 5 結果 " + (10 != 5));
  9. }
  10. }

程式的執行如下所示:

  1. 10 > 5 結果 true
  2. 10 >= 5 結果 true
  3. 10 < 5 結果 false
  4. 10 <= 5 結果 false
  5. 10 == 5 結果 false
  6. 10 != 5 結果 true

比較運算在使用時有個即使是程式設計老手也可能犯的錯誤,且不容易發現,也就是等於運算子 ‘==’,注意它是兩個連續的等號 ‘=’ 所組成,而不是一個等號,一個等號是指定運算,這點必須特別注意,例如若有兩個變數x與y要比較是否相等,應該是寫成 x == y,而不是寫成 x = y,後者的作用是將y的值指定給 x,而不是比較運算 x 與 y 是否相等。

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

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

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

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

範例3.15 ConditionalOperator.java

  1. import java.util.Scanner;
  2. public class ConditionalOperator {
  3. public static void main(String[] args) {
  4. Scanner scanner = new Scanner(System.in);
  5. System.out.print("請輸入學生分數: ");
  6. int scoreOfStudent = scanner.nextInt();
  7. System.out.println("該生是否及格? " +
  8. (scoreOfStudent >= 60 ? '是' : '否'));
  9. }
  10. }

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

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

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

範例3.16 OddDecider.java

  1. import java.util.Scanner;
  2. public class OddDecider {
  3. public static void main(String[] args) {
  4. Scanner scanner = new Scanner(System.in);
  5. System.out.print("請輸入數字: ");
  6. int number = scanner.nextInt();
  7. System.out.println("是否為奇數? " +
  8. (number%2 != 0 ? '是' : '否'));
  9. }
  10. }

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

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

3.3.5 邏輯、位元運算

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

範例3.17 LogicalOperator.java

  1. public class LogicalOperator {
  2. public static void main(String[] args) {
  3. int number = 75;
  4. System.out.println((number > 70 && number < 80));
  5. System.out.println((number > 80 || number < 75));
  6. System.out.println(!(number > 80 || number < 75));
  7. }
  8. }

三段陳述句分別會輸出 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

  1. public class BitwiseOperator {
  2. public static void main(String[] args) {
  3. System.out.println("AND運算:");
  4. System.out.println("0 AND 0\t\t" + (0 & 0));
  5. System.out.println("0 AND 1\t\t" + (0 & 1));
  6. System.out.println("1 AND 0\t\t" + (1 & 0));
  7. System.out.println("1 AND 1\t\t" + (1 & 1));
  8. System.out.println("\nOR運算:");
  9. System.out.println("0 OR 0\t\t" + (0 | 0));
  10. System.out.println("0 OR 1\t\t" + (0 | 1));
  11. System.out.println("1 OR 0\t\t" + (1 | 0));
  12. System.out.println("1 OR 1\t\t" + (1 | 1));
  13. System.out.println("\nXOR運算:");
  14. System.out.println("0 XOR 0\t\t" + (0 ^ 0));
  15. System.out.println("0 XOR 1\t\t" + (0 ^ 1));
  16. System.out.println("1 XOR 0\t\t" + (1 ^ 0));
  17. System.out.println("1 XOR 1\t\t" + (1 ^ 1));
  18. }
  19. }

執行結果就是各個位元運算的結果:

  1. AND運算:
  2. 0 AND 0 0
  3. 0 AND 1 0
  4. 1 AND 0 0
  5. 1 AND 1 1
  6. OR運算:
  7. 0 OR 0 0
  8. 0 OR 1 1
  9. 1 OR 0 1
  10. 1 OR 1 1
  11. XOR運算:
  12. 0 XOR 0 0
  13. 0 XOR 1 1
  14. 1 XOR 0 1
  15. 1 XOR 1 0

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

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

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

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

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

範例3.19 OddDecider2.java

  1. import java.util.Scanner;
  2. public class OddDecider2 {
  3. public static void main(String[] args) {
  4. Scanner scanner = new Scanner(System.in);
  5. System.out.print("請輸入數字: ");
  6. int number = scanner.nextInt();
  7. System.out.println("是否為奇數? " +
  8. ((number&1) != 0 ? '是' : '否'));
  9. }
  10. }

執行結果:

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

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

  1. 整數400000100
  2. 整數100000001
  3. AND 運算後:00000000

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

  1. 整數300000011
  2. 整數100000001
  3. AND運算後:00000001

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

範例3.20 XorCode.java

  1. public class XorCode {
  2. public static void main(String[] args) {
  3. char ch = 'A';
  4. System.out.println("編碼前:" + ch);
  5. ch = (char)(ch^7);
  6. System.out.println("編碼後:" + ch);
  7. ch = (char)(ch^7);
  8. System.out.println("解碼:" + ch);
  9. }
  10. }

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

  1. ASCII 中的 'A' 字元編碼為 6501000001
  2. 整數 700000111
  3. XOR 運算後:01000110

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

  1. 編碼前:A
  2. 編碼後:F
  3. 解碼: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

  1. public class ShiftOperator {
  2. public static void main(String[] args) {
  3. int number = 1;
  4. System.out.println( "2的0次: " + number);
  5. number = number << 1;
  6. System.out.println("2的1次: " + number);
  7. number = number << 1;
  8. System.out.println("2的2次: " + number);
  9. number = number << 1;
  10. System.out.println("2的3次:" + number);
  11. }
  12. }

執行結果:

  1. 20次: 1
  2. 21次: 2
  3. 22次: 4
  4. 23次:8

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

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

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

3.3.6 遞增、遞減運算

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

  1. int i = 0;
  2. i = i + 1;
  3. System.out.println(i);
  4. i = i - 1;
  5. System.out.println(i);

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

範例 3.22 IncrementDecrement.java

  1. public class IncrementDecrement {
  2. public static void main(String[] args) {
  3. int i = 0;
  4. System.out.println(++i);
  5. System.out.println(--i);
  6. }
  7. }

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

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

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

範例 3.23 IncrementDecrement2.java

  1. public class IncrementDecrement2 {
  2. public static void main(String[] args) {
  3. int i = 0;
  4. int number = 0;
  5. number = ++i; // 相當於i = i + 1; number = i;
  6. System.out.println(number);
  7. number = --i; // 相當於i = i - 1; number = i;
  8. System.out.println(number);
  9. }
  10. }

在這段程式中,number 的值會前後分別顯示為 1 與 0,再看看範例 3.24。

範例 3.24 IncrementDecrement3.java

  1. public class IncrementDecrement3 {
  2. public static void main(String[] args) {
  3. int i = 0;
  4. int number = 0;
  5. number = i++; // 相當於number = i; i = i + 1;
  6. System.out.println(number);
  7. number = i--; // 相當於 number = i; i = i - 1;
  8. System.out.println(number);
  9. }
  10. }

在這段程式中,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

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

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

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

  1. int i = 5;
  2. arr[--i %= 10] = 10;

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

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

  1. int i=0;
  2. int number=0;
  3. number=i++;
  4. number=i--;

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

  1. int i = 0;
  2. int number = 0;
  3. number = i++;
  4. number = i--;

3.4 流程控制

現實生活中待解決的事千奇百怪,在電腦發明之後,想要使用電腦解決的需求也是各式各樣:「如果」發生了…,就要…;「對於」…,就一直執行…;「如果」…,就「中斷」…。為了告訴電腦在特定條件下該執行的動作,您要會使用各種條件式來定義程式執行的流程。

3.4.1 if 條件式

為了應付「如果」發生了 …,就要 … 的需要,Java 提供了 if 條件式 ,它的語法如下:

  1. if(條件式)
  2. 陳述句一;
  3. else
  4. 陳述句二;

這個語法的意思,白話來說,就是當「條件式」成立時(true),則執行「陳述句一」,要不然(else)就執行「陳述句二」;如果條件式不成立時並不想作任何事,則 else 可以省略。

在 if 後如果有兩個以上陳述句,稱之為「複合陳述句」(Compound statement),此時必須使用 { } 定義區塊(Block)將複合陳述句包括起來,例如:

  1. if(條件式) {
  2. 陳述句一;
  3. 陳述句二;
  4. }
  5. else {
  6. 陳述句三;
  7. 陳述句四;
  8. }

範例 3.25 是個簡單的程式,使用if條件式來判斷使用者的輸入是奇數還是偶數。

範例 3.25 OddDecider3.java

  1. import java.util.Scanner;
  2. public class OddDecider3 {
  3. public static void main(String[] args) {
  4. Scanner scanner = new Scanner(System.in);
  5. System.out.print("請輸入數字: ");
  6. int input = scanner.nextInt();
  7. int remain = input % 2; // 求除 2 的餘數
  8. if(remain == 1) // 如果餘數為1
  9. System.out.println(input + "為奇數");
  10. else
  11. System.out.println(input + "為偶數");
  12. }
  13. }

在 if 中您也可以再設定執行的條件,例如:

  1. if(條件式一) {
  2. 陳述句一;
  3. if(條件式二)
  4. 陳述句二;
  5. 陳述句三;
  6. }

這只個簡單的例子,其中陳述句二要執行,必須同時滿足「條件式一」與「條件式二」才行;再來看個例子:

  1. if(條件式一) {
  2. 陳述句一;
  3. // 其它陳述句
  4. }
  5. else
  6. if(條件式二)
  7. 陳述句二;

如果「條件式一」不滿足,就會執行 else 中的陳述,而您在這邊進行「條件式二」的測試,如果滿足就執行「陳述句二」,由於 Java 是個自由格式語言,您可以適當的排列這個程式,這會比較好懂一些:

  1. if(條件式一) {
  2. 陳述句一;
  3. // 其它陳述句
  4. }
  5. else if(條件式二)
  6. 陳述句二;

基於這個方式,您可以如下設定多個條件,且易讀易懂:

  1. if(條件式一)
  2. 陳述一;
  3. else if(條件式二)
  4. 陳述句二;
  5. else if(條件式三)
  6. 陳述句三;
  7. else
  8. 陳述句四;

「陳述句四」會在條件式一、二、三都不成立時執行;範例3.26是個簡單的例子,處理學生的成績等級問題。

範例3.26 ScoreLevel.java

  1. import java.util.Scanner;
  2. public class ScoreLevel {
  3. public static void main(String[] args) {
  4. Scanner scanner = new Scanner(System.in);
  5. System.out.print("輸入分數:");
  6. int score = scanner.nextInt();
  7. if(score >= 90)
  8. System.out.println("得A");
  9. else if(score >= 80 && score < 90)
  10. System.out.println("得B");
  11. else if(score >= 70 && score < 80)
  12. System.out.println("得C");
  13. else if(score >= 60 && score < 70)
  14. System.out.println("得D");
  15. else
  16. System.out.println("得E(不及格)");
  17. }
  18. }

執行結果如下:

  1. 輸入分數:88
  2. B

在這邊要注意的是 if 與 else 的配對問題,例如以下程式依縮排來看,您覺得有無問題存在?

  1. if(條件式一)
  2. if(條件式二)
  3. 陳述句一;
  4. else
  5. 陳述句二;

很多人都會以為「條件式二」的 if 會與 else 配對,但事實上是「條件式一」的 if 與 else 配對,加上括號的話就不會誤會了:

  1. if(條件式一) {
  2. if(條件式二)
  3. 陳述句一;
  4. else
  5. 陳述句二;
  6. }

如果想避免這種錯誤,在程式中多使用括號是必要的,多寫一些總是比少寫一些來得保險一點。

注意到了嗎?在撰寫程式時適當的使用縮排(Indent),可以在定義程式區塊時讓區塊範圍看來容易分辨。

每個開發人員使用縮排的方式各不相同,您可以使用兩個空白或是四個空白,也可以按下Tab鍵來進行縮排。

良葛格的話匣子 我習慣的縮排方式是按四次空白鍵,不習慣按 Tab 鍵,因為各個文字編輯器或 IDE 對於 Tab 字元的顯示方式都不太一樣,有些文字編輯器或 IDE 預設使用 Tab 字元來自動縮排的話,我都還會將之改為預設四個空白字元進行縮排,因為空白字元顯示方式是一致的。

3.4.2 switch 條件式

switch 只能比較數值或字元,不過別以為這樣它就比 if 來得沒用,使用適當的話,它可比 if 判斷式來得有效率;switch 的語法架構如下:

  1. switch(變數名稱或運算式) {
  2. case 符合數字或字元:
  3. 陳述句一;
  4. break;
  5. case 符合數字或字元:
  6. 陳述句二;
  7. break;
  8. default:
  9. 陳述三;
  10. }

首先看看 switch 的括號,當中置放您要取出數值的變數,取出數值之後,程式會開始與 case 中所設定的數字或字元作比對,如果符合就執行當中的陳述句,直到遇到 break 後離開 switch 區塊,如果沒有符合的數值或字元,則會執行 default 後的陳述句,default 不一定需要,如果沒有預設要處理的動作,您可以省去這個部份。

來看看範例 3.26 的成績等級比對如何使用 switch 來改寫。

範例 3.27 ScoreLevel2.java

  1. import java.util.Scanner;
  2. public class ScoreLevel2 {
  3. public static void main(String[] args) {
  4. Scanner scanner = new Scanner(System.in);
  5. System.out.print("請輸入分數: ");
  6. int score = scanner.nextInt();
  7. int level = (int) score/10;
  8. switch(level) {
  9. case 10:
  10. case 9:
  11. System.out.println("得A");
  12. break;
  13. case 8:
  14. System.out.println("得B");
  15. break;
  16. case 7:
  17. System.out.println("得C");
  18. break;
  19. case 6:
  20. System.out.println("得D");
  21. break;
  22. default:
  23. System.out.println("得E(不及格)");
  24. }
  25. }
  26. }

在這個程式中,您使用除法並取得運算後的商數,如果大於 90 的話,除以 10 的商數一定是 9 或 10(100 分時),在 case 10 中沒有任何的陳述,也沒有使用 break,所以會繼續往下執行,直到遇到 break 離開 switch 為止,所以學生成績 100 分的話,也會顯示 A 的成績等級;如果比對的條件不在10到6這些值的話,會執行 default 下的陳述,這表示商數小於 6,所以學生的成績等級就顯示為 E 了。

注意在 case 後的等號是冒號而不是分號,這是個很常打錯的符號;如果您比對的是字元,則記得加上單引號(’ ‘),例如:

  1. case 'A':

這個程式與使用 if 來判斷成績等級的程式有何不同?如果純綷比對數字或字元的話,建議使用 switch,因為它只會在一開始的 switch 括號中取出變數值一次,然後將這個值與下面所設定的 case 比對,但如果您使用if的話,每次遇到條件式時,都要取出變數值,效率的差異就在這,例如:

  1. if(a == 1)
  2. // ....
  3. else if(a == 2)
  4. // ....
  5. else if(a == 3)
  6. // ....

這個程式片段在最差的狀況下,也就是 a = 3 時,共需三次比對,而每次比對都必須取出變數 a 的值一次,如果換成 switch 的話:

  1. switch(a) {
  2. case 1:
  3. // ..
  4. break;
  5. case 2:
  6. // ..
  7. break;
  8. case 3:
  9. // ..
  10. break;
  11. }

在這個程式片段中,您只在一開頭 switch 的括號中取出變數 a 的值,然後逐一比對下面的 case,效率的差別就在於這邊;當然並不是使用 if 就不好,遇到複合條件時,switch 就幫不上忙了,您無法在 switch 中組合複雜的條件陳述,這時就得使用 if 了,簡單的說,if 與 switch 兩者可以搭配著靈活使用。

3.4.3 for 迴圈

在 Java 中如果要進行重複性的指令執行,可以使用 for 迴圈式,它的基本語法如下:

  1. for(初始式; 判斷式; 遞增式) {
  2. 陳述句一;
  3. 陳述句二;
  4. }

如果陳述句只有一個,也就是非複合陳述句,{ } 可以省略不寫;for 迴圈的「第一個初始陳述句只會執行一次」,之後每次重新進行迴圈時,都會「根據判斷式來判斷是否執行下一個迴圈」,而每次執行完迴圈之後,都會「執行遞增式」。

實際看看範例 3.28 來瞭解 for 迴圈的功能。

範例3.28 SimpleLoopFor.java

  1. public class SimpleLoopFor {
  2. public static void main(String[] args) {
  3. for(int i = 0; i < 10; i++)
  4. System.out.print(" " + i);
  5. }
  6. }

執行結果:

  1. 0 1 2 3 4 5 6 7 8 9

這是一個簡單的例子,但說明 for 的作用再適合不過,在 Java 中您可以直接在 for 中宣告變數與指定初始值,這個宣告的變數在 for 迴圈結束之後也會自動消失;初始變數的陳述句只被執行一次,接下來迴圈會根據 i 是否小於 10 來判斷是否執行迴圈,而每執行一次迴圈就將 i 加 1。

for 迴圈中也可以再使用 for 迴圈,初學者很喜歡用的例子就是顯示九九乘法表,這邊就用這個例子來說明:

範例3.29 NineTable.java

  1. public class NineTable {
  2. public static void main(String[] args) {
  3. for(int j = 1; j < 10; j++) {
  4. for(int i = 2; i < 10; i++) {
  5. System.out.printf("%d*%d=%2d ",i, j, i * j);
  6. }
  7. System.out.println();
  8. }
  9. }
  10. }

執行結果:

  1. 2*1= 2 3*1= 3 4*1= 4 5*1= 5 6*1= 6 7*1= 7 8*1= 8 9*1= 9
  2. 2*2= 4 3*2= 6 4*2= 8 5*2=10 6*2=12 7*2=14 8*2=16 9*2=18
  3. 2*3= 6 3*3= 9 4*3=12 5*3=15 6*3=18 7*3=21 8*3=24 9*3=27
  4. 2*4= 8 3*4=12 4*4=16 5*4=20 6*4=24 7*4=28 8*4=32 9*4=36
  5. 2*5=10 3*5=15 4*5=20 5*5=25 6*5=30 7*5=35 8*5=40 9*5=45
  6. 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36 7*6=42 8*6=48 9*6=54
  7. 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49 8*7=56 9*7=63
  8. 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64 9*8=72
  9. 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81

事實上,for 迴圈的語法其實只是將三個複合陳述區塊寫在一個括號中而已,所不同的是第一個陳述區塊只會執行一次,第二個陳述區塊專司判斷是否繼續下一個迴圈,而第三個陳述區塊只是一般的陳述句,而不一定只放遞增運算式。

for 括號中的每個陳述區塊是以分號 ‘;’ 作區隔,而在一個陳述區塊中若想寫兩個以上的陳述句,則使用逗號 ‘,’ 作區隔,有興趣的話,看看範例 3.30 中九九乘法表的寫法,它只使用了一個 for 迴圈就可以完成九九乘法表的列印,執行結果與範例 3.29 是一樣的。

範例 3.30 NineTable2.java

  1. public class NineTable2 {
  2. public static void main(String[] args) {
  3. for (int i = 2, j = 1;
  4. j < 10;
  5. i = (i==9)?((++j/j)+1):(i+1)) {
  6. System.out.printf("%d*%d=%2d%c", i, j, i * j,
  7. (i==9 ? '\n' : ' '));
  8. }
  9. }
  10. }

這個程式的重點在於讓您看看 for 迴圈的括號中之寫法,當然原則上並不鼓勵這麼寫,程式基本上還是以清楚易懂為原則,至於這個九九乘法表的演算其實也不難,當中的語法之前都介紹過了,您可以親自演算看看就知道怎麼回事了。

3.4.4 while 迴圈

Java 提供 while 迴圈式,它根據您所指定的條件式來判斷是否執行迴圈本體,語法如下所示:

  1. while(條件式) {
  2. 陳述句一;
  3. 陳述句二;
  4. }

如果迴圈本體只有一個陳述句,則 while 的 { } 可以省略不寫;while 像是沒有起始陳述與終止陳述的 for 迴圈,主要用於重複性的動作,而停止條件未知何時發生的情況,例如一個使用者輸入介面,使用者可能輸入 10 次,也可能輸入 20 次,這時迴圈停止的時機是未知的,您可以使用 while 迴圈來作這個事。

一個計算輸入成績平均的程式如範例 3.31 所示。

範例 3.31 ScoreAverage.java

  1. import java.util.Scanner;
  2. public class ScoreAverage {
  3. public static void main(String[] args) {
  4. Scanner scanner = new Scanner(System.in);
  5. int score = 0;
  6. int sum = 0;
  7. int count = -1;
  8. while(score != -1) {
  9. count++;
  10. sum += score;
  11. System.out.print("輸入分數(-1結束):");
  12. score = scanner.nextInt();
  13. }
  14. System.out.println("平均:" + (double) sum/count);
  15. }
  16. }

在這個程式中,使用者的輸入次數是未知的,所以您使用 while 迴圈來判斷使用者的輸入是否為 -1,以作為迴圈執行的條件,一個執行的例子如下:

  1. 輸入分數(-1結束):99
  2. 輸入分數(-1結束):88
  3. 輸入分數(-1結束):77
  4. 輸入分數(-1結束):-1
  5. 平均:88.0

while 可以用作無窮迴圈,很多地方都用到的到無窮迴圈,例如遊戲設計中對使用者輸入裝置的輪詢(poll),或是動畫程式的播放都會使用到無窮廻圈,一個無窮迴圈如下所示:

  1. while(true) {
  2. 迴圈內容;
  3. ....
  4. }

無窮迴圈可以由自己迴圈中的某個條件式來結束,例如下面是一個迴圈內部終止的例子:

  1. while(true) {
  2. 陳述句;
  3. if(條件式)
  4. break; // 跳離迴圈
  5. ....
  6. }

當「條件式」成立時,會執行 break 離開 while 迴圈,這個 break 與 switch 中的作用是一樣的,都是要離開當時執行的程式區塊時使用。

while 迴圈有時稱之為「前測式迴圈」,因為它在迴圈執行前就會進行條件判斷,而另一個 do while 稱之「後測式迴圈」,它會先執行迴圈本體,然後再進行條件判斷,do while 的語法如下所示:

  1. do {
  2. 陳述句一;
  3. 陳述句二;
  4. ....
  5. } while(條件式);

注意 while 後面是以分號 ‘;’作為結束,這個很常被忽略;由於 do while 會先執行迴圈,所以它通常用於進行一些初始化或介面溝通的動作,例如範例 3.32 所示。

範例 3.32 OddDecider4.java

  1. import java.util.Scanner;
  2. public class OddDecider4 {
  3. public static void main(String[] args) {
  4. Scanner scanner = new Scanner(System.in);
  5. int input = 0;
  6. int replay = 0;
  7. do {
  8. System.out.print("輸入整數值:");
  9. input = scanner.nextInt();
  10. System.out.println("輸入數為奇數?" +
  11. ((input%2 == 1) ? 'Y': 'N'));
  12. System.out.print("繼續(1:繼續 0:結束)?");
  13. replay = scanner.nextInt();
  14. } while(replay == 1);
  15. }
  16. }

執行結果:

  1. 輸入整數值:77
  2. 輸入數為奇數?Y
  3. 繼續(1:繼續 0:結束)?0

3.4.5 break、continue

break 可以離開目前 switch、for、while、do while 的區塊,並前進至區塊後下一個陳述句,在 switch 中主要用來中斷下一個 case 的比對,在 for、while與do while 中,主要用於中斷目前的迴圈執行,break 的例子您之前已經看過不少,這邊不再舉例。

continue 的作用與 break 類似,主要使用於迴圈,所不同的是 break 會結束區塊的執行,而 continue 只會結束其之後區塊的陳述句,並跳回迴圈區塊的開頭繼續下一個迴圈,而不是離開迴圈,例如:

  1. for(int i = 1; i < 10; i++) {
  2. if(i == 5)
  3. break;
  4. System.out.println("i = " + i);
  5. }

這段程式會顯示 i = 1 到 i = 4,因為當 i 等於 5 時就會執行 break 而離開迴圈,再看下面這個程式:

  1. for(int i = 1; i < 10; i++) {
  2. if(i == 5)
  3. continue;
  4. System.out.println("i = " + i);
  5. }

這段程式會顯示 1 到 4,與 6 到 9,當 i 等於 5 時,會執行 continue 直接結束此次迴圈,這次迴圈中 System.out.println() 該行並沒有被執行,然後從區塊開頭執行下一次迴圈,所以 5 並沒有被顯示。

break 與 continue 還可以配合標籤使用,例如本來 break 只會離開 for 迴圈,設定標籤與區塊,則可以離開整個區塊,範例 3.33 舉個簡單的示範。

範例 3.33 BreakTest.java

  1. public class BreakTest {
  2. public static void main(String[] args) {
  3. back : {
  4. for(int i = 0; i < 10; i++) {
  5. if(i == 9) {
  6. System.out.println("break");
  7. break back;
  8. }
  9. }
  10. System.out.println("test");
  11. }
  12. }
  13. }

程式的執行結果會顯示 break;back 是個標籤,當 break back; 時,返回至 back 標籤處,之後整個 back 區塊不執行而跳過,所以這個程式 System.out.println(“test”); 不會被執行。

事實上 continue 也有類似的用法,只不過標籤只能設定在 for 之前,範例 3.34 舉個簡單的示範。

範例3.34 ContinueTest.java

  1. public class ContinueTest {
  2. public static void main(String[] args) {
  3. back1:
  4. for(int i = 0; i < 10; i++){
  5. back2:
  6. for(int j = 0; j < 10; j++) {
  7. if(j == 9) {
  8. continue back1;
  9. }
  10. }
  11. System.out.println("test");
  12. }
  13. }
  14. }

continue 配合標籤,可以自由的跳至任何一層 for 迴圈,您可以試試 continue back1 與 continue back2 的不同,設定 back1 時,System.out.println(“test”); 不會被執行。

3.5 接下來的主題

每一個章節的內容由淺至深,初學者該掌握的深度要到哪呢?在這個章節中,對於初學者我建議至少掌握以下幾點內容:

  • 能輸出訊息至文字模式
  • 能從文字模式下取得使用者的輸入
  • 懂得使用表 3.1、表 3.2 的格式字元
  • 得 Java 中的基本資料型態與變數宣告
  • 會使用各種運算子,位元運算的話視您的領域而定,如果不常用的話可以暫時忽略
  • 多使用 if、for、switch、while 作程式練習

程式語言本質上就是語言,就像中文、英文一樣,要學習語言必先學習文法,這個章節所介紹的就是 Java 語言的基本文法(語法),接下來就是多使用這種語言以求熟悉這個語言,多找些題目來練習是必要的,在練習的同時培養使用 Java 語言的邏輯來思考問題,您可以在我的網站上找到一些常見的程式題目來作練習:

接下來我會介紹 Java SE 下提供的一些標準物件如何使用,在您正式瞭解 Java 的物件導向程式設計之前,您可以先將物件理解為一個現成的工具,Java SE 下提供了許多標準工具可以使用,熟悉這些工具的使用也是學習 Java 的必備功課。