藉一個排序實例認識 Java 環境


你手上的工作適合用 Java 解嗎?

Sun 的 Java 是現在當紅的語言, 除了獲得諸如 IBM 等大公司的 支持 之外, 自由軟體界也有 豐富的資源

但筆者認為 java 紅得名過其實, 以致於許多人在不適當的場合誤用 java。 程式語言的優劣並不完全決定於它的功能是否強大; 常人往往忽略更重要的一點: 從學習到應用, 這個語言是否真的能夠提高程式設計師的生產力? 倚天劍與屠龍刀固然很酷, 我們不會拿它來切水果; 噴射機固然很酷, 到巷子口買早餐還是騎 腳踏車 比較方便。 更何況寶刀還有干將莫邪; 交通工具還有深海潛艇, 在不同的場合, 可能更適用。 最後, 還有一點政治因素, 請見 Richard Stallman 的文章: Free but shackled: The Java trap

撇開政治不談, 純粹從技術角度來看, 我還是認為 java 並不適合日常生活使用。 即使是對資訊科系的同學而言, 拿它來當做入門的語言, 都嫌太沉重。 寫程式要有成就感才會有樂趣。 光是寫個 "hello world!" 就要先三跪九叩, 定義 class 與 method, 如果我第一個接觸的程式語言是 java, 老早就被嚇壞, 對寫程式完全喪失信心。 我很慶幸自己沒有生在盲目崇拜 java 的年代。

最值得資訊科系同學拿來入門, 最值得其他非資訊科系同學拿來玩票學寫程式的, 應該是 scripting 類的程式語言。 過去被 java 嚇到失去寫程式興趣與信心的同學, 建議從這類語言著手, 三五行就可以寫出有用的程式, 你會發現寫程式可以是很有成就感的事 (如果是 perl, 一句話就可以做很多有用的事)。 隔了很久以後, 也許你會發覺其實 java 的複雜設計確實有它的好處, 特別適用於撰寫龐大, 需要模組化的系統。

以下幾篇批評 java 的文章請參考:

  1. Why I am not a Java Programmer (或見 庫存頁面): 指出 java 與 perl 背後設計哲學的不同, 請讀者自行判斷選擇。 極佳文章。
  2. Softpanorama Java Critique Page: java 像 cobol 一樣, 不完美, 但符合商業需要, 所以比其他技術上更優秀的語言流行。
  3. A Java Critique: Java 較諸 C/C++ 的有餘與不足, 以及概括承受其既有缺點的沉重包袱。

有一類場合 java 倒是非常適合: 執行速度是重要考量, 程式碼會重複利用, 介面明確/簡單/清楚, 的函式庫。 具體地說, 資料結構就是一個使用 java 絕佳的場合。 這篇講義的目的就是為了配合資料結構課程, 介紹一下純自由軟體的 java 環境; 將來或許會增加其他話題。 但可以確定的是: 它永遠不會變成 java 入門的教學講義。 讀者必須先自行熟悉物件導向的觀念, 與 Java 語言基本要素。 (可是我自己也還沒有學 java 耶...)

呼應 rms 的觀點, 我們只推薦純自由軟體的 Java 環境。 至少有兩個選擇如下:

  1. 安裝 自由軟體基金會 (fsf) gnu 計劃的 gcj。 在 Mandrake Linux 上, 主要是 gcj-tools 與 gcc-java 兩個套件, 及其他一堆相依套件。
  2. 或是安裝 kaffe 作為 compiler 及 VM。 在 Mandrake Linux 上, 直接安裝 kaffe 套件就可以了。 雖然文件只說 kaffe 是一個 VM, 但其實它也可以編譯原始碼。

以下假設你已安裝好 gnu 版或 kaffe 版的 java。

實驗一: 編譯自己寫的軟體

  1. 編譯 FSUArray 函式庫: javac FSUArray.java 並確認在目前目錄內產生了一個虛擬機器的機器碼檔 (byte code of the virtual machine) FSUArray.class 注意: 編譯時必須給副檔名!
  2. 編譯 SortDemo 主程式: javac SortDemo.java 並確認在目前目錄內產生了一個虛擬機器的機器碼檔 SortDemo.class 一樣, 編譯時必須給副檔名。 如果你用的是 gnu 版, 可能會出現 "找不到 FSUArray" 之類的訊息, 改試試看: javac -classpath . SortDemo.java
  3. 執行程式: java SortDemo guava orange apple grape kiwi banana 應該印出排序後的結果, 每列一個字串。 注意: 執行時不可給副檔名!

如果是在 cygwin 底下使用 gcc-java, 下指令的方式有一點不同。 其實 gcj 是 gcc 的前置處理器, 所以最後透過 gcc 產生出來的是執行檔, 而不是 java byte code。 例如你有一個 SetExample.java 程式, 可以這樣編譯: gcj --main=SetExample SetExample.java 檢查一下, 發現產生了一個 a.exe 檔, 直接就可以執行: ./a.exe

java 程式的編譯與執行流程圖示

實驗二: 使用既有函式庫的介面

同樣的程式碼, 但現在要改用 Williams 所定義的 PriorityQueue 介面。 請先下載威廉的函式庫 data-structures.jar, 放在某目錄底下。

  1. 刪除原有的 byte code: rm *.class
  2. 按照註解修改 FSUArray.java (兩處) 及 SortDemo.java (兩處)
  3. 重新編譯: javac FSUArray.java 注意看看有什麼錯誤訊息?
  4. 把 Williams 寫的函式庫, 及目前目錄, 都加入搜尋路徑: export CLASSPATH=某目錄/data-structures.jar:. 注意最後面的句點不要打漏了, 這代表 "目前目錄"。 而且全部打在同一列上, 除了 export 與 CLASSPATH 之間有一個空格, 其他地方都不能有空格。 如果是 kaffe 版, 還要將系統內定的函式庫路徑也一併寫出來: export CLASSPATH=/usr/lib/kaffe/lib/rt.jar:某目錄/data-structures.jar:. 這句 export 只要下一次, 就相當於每次在命令列上給 -classpath 這個選項, 省去編譯時的麻煩。 但是如果離開 shell, 下次再進來時, 還是必須再打一次。 若將這句話放在 .bashrc 裡面, 就可以一勞永逸。
  5. 編譯 FSUArray 函式庫: javac FSUArray.java 產生 FSUArray.class 這個虛擬機器碼檔案。 如果是 kaffe 版, 會自動放在 DataStructures/ 子目錄底下; 如果是 gnu 版, 則是放在目前目錄下, 請手動建立子目錄並將檔案搬進去, 不然等一下執行時會出錯。
  6. 編譯 主程式: javac SortDemo.java 產生 SortDemo.class 檔案。
  7. 執行程式: java SortDemo guava orange apple grape kiwi banana 應該印出排序後的結果, 每列一個字串。

java 程式的編譯與執行 -- 使用既有的函式庫的介面

實驗三: 使用既有函式庫的實作

這次我們不只使用 Williams 所寫的 PriorityQueue 介面, 還要使用他所寫的 BinaryHeap 類別。 (不論新增或刪除都不花太多時間, 一個很有效率的類別。) 至於我們自己寫的 FSUArray 將被我們抽換掉, 完全用不到了。

  1. 刪除 FSUArray.class, 再執行一次 SortDemo, 應該會出錯。 請仔細看: 錯誤訊息說什麼?
  2. 把這句
    PriorityQueue pq = (PriorityQueue) new FSUArray();
    裡面的 FSUArray 改成 BinaryHeap
  3. 重新編譯, 再執行一次。

現在改用威廉提供的 BinaryHeap, 把我寫的 FSUArray 替換掉

實驗四: 如果要對複雜的資料結構排序呢?

假設我們要對 學生成績 排序。 請編譯並執行 CmpDemo.java。 現在你的程式到底用到那些類別呢? FSUArray.java 還有用到嗎?

作業:

  1. 請寫一個類別 SortedArray 用以取代 FSUArray。 裡面的資料由大到小按順序排列, 新增時必須視資料大小將之安插到正確位置; 刪除時則永遠刪除並傳回最小, 也就是最後面那個元素。 一樣用 SortDemo 程式測試你的類別, 先只使用 SortDemo 與 SortedArray; 再改採用 Williams 所寫的 PriorityQueue 介面, 重新編譯/執行一次。
  2. 有書籍資料數筆, 包含書名, 作者, 價格, 頁數等資訊, 例如:
        Perl 學習手冊 第三版, Randal L. Schwartz and Tom Phoenix, 580 元, 380 頁
        Perl程式設計疑難排解, Martin Brown, 490 元, 512 頁
        ...
        Advanced Perl Programming, Sriram Srinivasan, 1225 元, 427 頁
    
    請模仿實驗四, 寫一個程式 (實際上是兩個 .java 檔) 對這些書本按照 「每頁的價格」 排序。 (當然這個順序與書籍的好壞沒有特別的關係; 只是隨便舉的例子)