SCJP ch13

248 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
248
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
8
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

SCJP ch13

  1. 1. 第 13 章 套件 (Packages) 本投影片(下稱教用資源)僅授權給採用教用資源相關之旗標書籍為教科書之授課老師(下稱老師)專用,老 師為教學使用之目的,得摘錄、編輯、重製教用資源(但使用量不得超過各該教用資源內容之 80% )以製作為 輔助教學之教學投影片,並於授課時搭配旗標書籍公開播放,但不得為網際網路公開傳輸之遠距教學、網路教 學等之使用;除此之外,老師不得再授權予任何第三人使用,並不得將依此授權所製作之教學投影片之相關著 作物移作他用。 著作權所有 © 旗標出版股份有限公司
  2. 2. 學習目標  瞭解套件 (Packages)  適當的組織程式  撰寫通用於多個程式的公用類別  使用套件避免名稱的重複  將套件打包為 JAR 壓縮檔  熟悉編譯與執行時的參數設定
  3. 3. 前言  當我們撰寫的程式越來越複雜、程式碼也愈來愈多行 的時候 , 會發現將所有的類別全部寫在同一個檔案中 並不是個好作法 , 因為這會讓整個程式看起來很複雜 , 想要找到某個類別的某個方法時也必須耗費一番功 夫。  此外 , 有些類別 , 例如前幾章範例中出現的二分搜尋 法或排序的類別 , 在設計時 , 就是希望可以提供給不 同的程式使用 , 如果直接寫在個別程式檔中 , 就無法 凸顯該類別可供不同程式採用的特性。
  4. 4. 前言  更進一步來看 , 如果類別要分享給其他人使用 , 那麼 很可能發生我們寫的類別與他人寫的類別名稱衝突的 情況。  但是別人不一定能夠修改我們的類別名稱 ( 沒有原始 碼 ), 使對方被迫修改可表達類別意義的名稱。  在這一章中 , 就要針對上述的問題 , 說明 Java 所提 供的解決方案 ― 套件 , 討論適當組織程式以及分享 特定類別的方法。
  5. 5. 13-1 程式的切割  當程式越來越大的時候 , 最簡單的整理方式就是讓每 一個類別單獨儲存在一個原始檔案中 , 並為個別的檔 案以所含的類別名稱為主檔名 ( 副檔名當然仍是 . java), 並將程式所需的檔案全部放在同一個目錄中。  舉例來說 , 上一章的幾何圖形類別範例 , 我們即可將 Shape 、 Circle 、 Cylinder 、 Rectangle 這幾個類 別都個別存於 Shape.java 、 Circle.java 、 Cylinder.java 、 Rectan gle.java 這四個檔案中。
  6. 6. 程式的切割  而需要用到這些類別的程式則自成一個檔案 , 並與前 述的檔案都放在一起 ( 您可以在書附光碟 Ch13/13-1 的資料夾中看到這些檔案 ) 。
  7. 7. 程式的切割  接著 , 當我們編譯 UsingOtherClasses.java 時 , Java 編譯器就會自動尋找含 Circle 、 Cylinder 、 Rectangle 類別的程式檔 , 並也 一併編譯這些檔案。  同樣在執行時 , "java" 執行程式也會自行找到相關的 類別檔 , 讓程式可正常執行。 ▪ 其實 Java 編譯器也會自動到環境變數 CLASSPATH 所設的路徑尋找類別檔 , 或是到執行 javac 時所指定 的路徑尋找 , 此部份會在本章稍後說明。
  8. 8. 程式的切割  使用單獨的檔案來儲存個別類別有以下好處: ▪ 管理類別更容易:只要找到與類別同名的檔案 , 就可 以找到類別的原始程式 , 而不需要去記憶哪一個檔案 中撰寫了那個類別 , 或者是某個類別究竟是在那個檔 案中。 ▪ 編譯程式更簡單:只要編譯包含有 main( ) 方法的類 別原始檔案 , 編譯器就會根據使用到的類別名稱 , 在 同一個資料夾下找出同名的原始檔 , 並進行必要的編 譯。以本例來說 , 雖然總共有 5 個原始檔案 , 但在編 譯 UsingOtherClasses.java 時 , Java 編譯器就會自 動發現程式中需要使用到其他 4 個類別 , 並自動編譯 這 4 個類別的原始程式檔。
  9. 9. 程式的切割  也就是說 , 雖然程式切割為多個檔案 , 但是在編譯及 執行上並沒有增加任何的複雜度 , 反而讓程式更易於 管理。 ( 即使不切割程式 , 在編譯後仍是一個類別產 生一個同名的類別檔。 ) ▪ 請特別注意 , 這些檔案必須放置在同一個資料夾下 , 而 且檔名要和類別名稱一樣 , 否則 Java 編譯 TTIIPP 器 並不知道要到哪裡找尋所需類別的原始檔案 , 導致編譯 錯誤。
  10. 10. 程式的切割  如果原始檔名和類別名稱不同 , 則可先編譯所有需要 的原始檔 ( 使用 javac src1 src2, src3..., 也可用 * 做為萬用字元 ), 以產生和類別同名的類別檔 , 這樣 在編譯或執行主類別時 , 就不會找不到其他類別了。
  11. 11. 13-2 分享寫好的類別  有些情況下 , 我們所撰寫的類別也可提供給其他人使 用 , 尤其是在開發團隊中 , 讓開發人員不需重複撰寫 類似功能的類別。  要做到這一點 , 最簡單的方式 , 就是將該類別的 .java 或 .class 檔案複製到別人的電腦上 , 不過使 用這種方式時 , 對方的程式中不能有任何類別或是介 面和我們所提供的類別同名。  遇到這種狀況時 , 要不就是修改他自己的類別名稱 , 要不就是修改你所提供的類別名稱 , 不僅徒然浪費時 間 , 也同時增加了修改過程中發生錯誤的機會。
  12. 12. 分享寫好的類別  為了解決上述問題 , Java 提供套件 (Packages) 這 種可將類別包裝起來的機制。  將類別包裝成套件時 , 就相當於替類別貼上一個標籤 ( 套件名稱 ) , 而使用到套件中的類別時 , 即需指明套 件名稱 , 因此可與程式中自己撰寫的同名類別區隔開 來 , 而不會混淆 , 也沒有重新命名的問題。
  13. 13. 13-2-1 建立套件  要將類別包裝在套件中 , 必須在程式最開頭使用 package 敘述 , 標示出類別所屬的套件名稱 , 以 Shape 類別為例 , 如果要將之包裝在 "flag" 這個套 件中 , 可寫成:
  14. 14. 建立套件
  15. 15. 建立套件  除了第 1 行的 package 敘述外 , 還要注意第 3 行 的類別定義 , 將類別存取控制宣告為 public, 這是因 為如果不宣告為 public, 則該類別就無法由 package 以外的程式使用。  然而如上所述 , 我們要使用 package 的原因就是希 望解決類別提供給別人使用的問題 , 所以當然要將 package 中的類別宣告為 public 。 ▪ 如果 package 中有只供自己使用的類別 , 則可不宣 告為 public 。
  16. 16. 建立套件  其它幾個類別原始檔的修改方式也類似 , 都是加上 package 敘述 , 並將類別宣告為 public 即可 ( 修改 的結果可參考光碟中 Ch13/13-2/flag 資料夾中的檔 案)。  在各檔案都做好相同的修改 , 這些類別就算是都屬於 我們指定的 flag 套件了 , 未來即使類別名稱和其他 套件名稱重複 , 也完全沒有關係。
  17. 17. 13-2-2 編譯包裝在套件中的類別  當類別使用 package 敘述指明所屬的套件之後 , 在 編譯類別時必須有額外的處理 , 可以選擇以下兩種方 式來整理及編譯這些類別: ▪ 自行建立套件資料夾:在 Java 中 , 同一套件的類別 必須存放在與套件名稱相同的資料夾中 , 然後才能供 其他程式使用。例如我們先將類別原始檔都放在與套 件同名的 flag 資料夾 , 接著用 javac 一一編譯 , 編 譯好的 .class 檔案也會存於其中。
  18. 18. 編譯包裝在套件中的類別 ▪ 由編譯器建立套件資料夾:我們也可將建立套件資料 夾的動作交給 javac 編譯器處理 , 只需在編譯時用參 數 -d 指定編譯結果的儲存路徑 , 編譯器就會根據 package 敘述指定的套件名稱 , 在指定的路徑下建立 與套件同名的子資料夾 , 並將編譯結果存到該資料夾 中。舉例來說 , 如果要將 flag 套件放在 C:Ch13132 的資料夾下 , 那麼就可以在編譯 Shape.java 時執 行如下的命令:
  19. 19. 編譯包裝在套件中的類別 javac 的 -d 參數原本是用來指定存放編譯結果的路 徑 , 在編譯不含 package 敘述的程式時 , 編譯好 的 .class 檔會直接存於 -d 參數所指的路徑中;但編 譯像 Shape.java 等第一行有 package 敘述的程式 時 , 則會在指定的路徑下 , 另建套件資料夾 , 以上例 而言 , 就是在 C:Ch1313-2 之下建立 flag 子資料夾 , 並將編譯好的 .class 檔案放到 flag 資料夾中。 ▪ 使用 -d 選項時 , 所指定要放置套件的資料夾必須已 經存在 , 否則編譯時會發生錯誤。以剛剛的範例來說 , 就是 C:Ch1313-2 這個資料夾必須已經存在。
  20. 20. 編譯包裝在套件中的類別  無論使用哪一種方式 , 一旦編譯好類別檔案 , 並且放 置到正確的資料夾中 , 其他程式就可以使用包在套件 中的類別了。
  21. 21. 13-2-3 使用套件中的類別  要使用套件中的類別時 , 必須以其完整名稱 (Fully Qualified Name) 來表示 , 其格式為『套件名稱 . 類 別名稱』。  以使用 flag 套件中的 Circle 類別為例 , 就必須使 用 flag.Circle 來表示 , 請看以下的程式。
  22. 22. 使用套件中的類別 ▪ 此檔案存於範例程式資料夾 Ch1313-2 之中。
  23. 23. 使用套件中的類別  在第 3 〜 5 行中 , 不論是物件宣告或是呼叫建構方 法時 , 都是使用『 flag. 類別名稱』的語法來使用 flag 套件中的類別。  而編譯及執行 UsingPackage.java 時 , 由於使用到 套件中的類別 , 所以必須要有額外的處理 , 我們區分 為兩種狀況來說明。
  24. 24. 套件與程式檔 在同一資料夾下  如果套件和使用到套件的程式 ( 例如 UsingPackage.java) 都放在同一資料夾下 , 那麼仍 是以一般的方式編譯、執行即可。  例如 UsingPackage.java 存於 Ch1313-2 資料夾 中 , 而 flag 套件則是存於同一路徑下的 flag 子資 料夾 , 那麼要編譯及執行 UsingPackage.java 就可 以這樣做:
  25. 25. 套件與程式檔 在同一資料夾下  當 Java 編譯器及虛擬機器看到 flag.* 的類別名稱 時 , 就會自動到程式所在的資料夾尋找是否有 flag 子資料夾。  如果有 , 就在此資料夾下尋找是否有所指定的類別檔 案 , 並確認此類別屬於 flag 套件。  如果一切無誤 , 就可以正常執行 , 否則就會出現編譯 錯誤。  舉例來說 , 如果 flag 資料夾中沒有 Rectangle.java 或 Rectangle.class 檔案 , 編譯時會出現如下錯誤。
  26. 26. 套件與程式檔 在同一資料夾下
  27. 27. 套件與程式檔 在同一資料夾下  此外如果編譯好後 , 不小心將 flag 資料夾中的檔案 移動位置或刪除 , 則執行程式時也會出現找不到類別 的訊息。  如果只是移動了位置 , 可利用下一小節的方式讓 java 可找到套件的類別檔。
  28. 28. 套件與程式在不同資料夾下  由於套件多半是提供給多人或是多個不同的程式共用 , 所以當套件發展完畢時 , 一般都會放置在某個特定 的位置 , 以方便不同的程式取用。  也就是說 , 套件所在的資料夾通常並非使用該套件的 程式所在的資料夾 , 此時在編譯及執行程式時 , 就必 須告訴 Java 編譯器以及 Java 虛擬機器套件所在 的位置。
  29. 29. 套件與程式在不同資料夾下  舉例來說 , 如果 flag 套件資料夾是放在 C:Allpackages 之下 , 而 UsingPackage.java 仍是 放在前述的範例路徑中 , 那麼要編譯與執行時 , 就必 須用 -cp 選項指出 flag 套件所在的路徑:
  30. 30. 套件與程式在不同資料夾下  其中 , -cp 選項意指 classpath, 也就是類別所在的 路徑 , Java 編譯器以及虛擬機器會到此選項所指定 的資料夾中尋找所需的類別檔案。  若需要列出多個路徑 , 可用分號連接 , 如此 Java 編 譯器以及虛擬機器就會依照指定的順序 , 依序到各個 資料夾中找尋所需的類別 , 如果程式使用到了位於不 同資料夾的套件或是類別 , 就必須如此指定。
  31. 31. 套件與程式在不同資料夾下  本例由於執行時所需的 UsingPackage.class 檔是位 在自己的資料夾 , 因此還要在 cp 選項所列的路徑中 加上 "." , 否則會因為在 C:Allpackages 下找不到 UsingPackage.class 檔而發生執行時期錯誤。 ▪ 一旦指定了 classpath, 就不會自動在目前的資料夾中 尋找類別檔了 , 因此別忘了在 classpath 中加上 "." 。 ▪ "." 代表目前目錄 , 而 ".." 則代表目前目錄的上一層 目錄。若是在 Unix/Linux 的系統 , 則路徑中的 "" 要 改成 "/", 而各路徑之間要以 : 分隔 ( 而非 ;) 。在 SCJP 考題中 , 有時會以 Unix 的路徑表示法來出題
  32. 32. 套件與程式在不同資料夾下  -cp 選項也可以使用全名寫成 -classpath, 兩者效用 相同。  如果常常會使用到某個資料夾下的套件或是類別 , 也 可以設定環境變數 classpath, 就不需要在編譯或是 執行時額外指定 -cp 或是 -classpath 選項 , 例如。
  33. 33. 套件與程式在不同資料夾下 ▪ Java 會優先搜尋內建類別所在的路徑 , 然後再搜尋 classpath 所指定的路徑。當 classpath 中有多個路徑 時 , 則會由最前面的路徑開始往後尋找 , 一旦找到所 需檔案即停止搜尋 , 而不管後面的路徑中是否還有同 名的檔案。
  34. 34. 13-3 子套件以及存取控制關係  當程式越來越大時 , 單一套件中可能會包含多個類別 。  舉例來說 , 上一章綜合演練所撰寫用來幫助陣列排序 的 Sort 類別也很適合放到套件中供其他人使用。  要做到這一點 , 只要將 Sort 類別以及伴隨的 ICanCompare 介面分別加上 package 敘述存檔編 譯即可。
  35. 35. 子套件以及存取控制關係 ▪ 再次提醒 , 需將編譯好的 .class 檔自行放到 flag 子 資料夾;或在編譯時加上 -d 選項。
  36. 36. 子套件以及存取控制關係  底下則是 Sort 類別:
  37. 37. 子套件以及存取控制關係
  38. 38. 子套件以及存取控制關係  這樣一來 , 所有的程式都可以利用 Sort 類別來幫陣 列排序了。  底下這個程式和上一章綜合演練的 Sorting.java 基 本上是一樣的 , 差別只在於 Sort 與 ICanCompare 現在是包裝在 flag 套件中 , 因此在程式中必須以 flag.Sort 以及 flag.ICanCompare 來識別。
  39. 39. 子套件以及存取控制關係
  40. 40. 子套件以及存取控制關係
  41. 41. 子套件以及存取控制關係
  42. 42. 子套件以及存取控制關係
  43. 43. 子套件以及存取控制關係
  44. 44. 13-3-1 在套件中建立子套件  如果加上原本就有的 Shape 第 4 個形狀類別 , 則 flag 套件就有 5 個類別、 1 個介面。  而當套件中的類別、介面愈來愈多時 , 為了進一步將 套件分類管理 , 我們可進一步在現有套件中建立子套 件 , 把相關的類別集合起來 , 歸於某一個子套件 , 而 這個子套件則和其他的類別或是子套件同屬於一個上 層套件中。
  45. 45. 在套件中建立子套件  舉例來說 , 如果要將 Shape 等形狀類別歸屬於 math 套件中 , 以表示其屬於數學幾何圖形類別的特 性 , 但又希望它仍舊屬於 flag 套件 , 以表示其為旗 標公司所提供 , 那麼就可以在 flag 套件中建立名稱 為 math 的子套件 , 並將形狀類別放入 math 子套 件中;而 ICanCompare 介面以及 Sort 類別則仍保 留在 flag 套件中。  要做到這一點 , 需將程式中的 package 敘述所設定 的套件名稱 , 改成『套件 . 子套件』的形式 , 例如。
  46. 46. 在套件中建立子套件 ▪ 此範例程式放置於 Ch1313-3 子資料夾中。
  47. 47. 在套件中建立子套件  這個版本和 13-1 節的版本完全一樣 , 只有一開頭的 package 敘述後面所指的套件名稱不同 , flag.math 這個名稱就表示其屬於 flag 套件下的子套件 math 。  子套件類別的 .class 檔也一樣要放在與子套件同名 的資料夾下 , 換句話說 , 我們必須將 Shape.class 檔需放在 flagmath 資料夾中。  由於牽涉了多層資料夾 , 因此要編譯這個程式 , 最簡 單的方式就是使用前面介紹過的 -d 選項 , 讓 Java 編譯器幫我們依據套件的結構建立對應的資料夾。
  48. 48. 在套件中建立子套件  假設要將 flag 套件的資料夾放在目前工作路徑下 , 那麼編譯的指令就是:  將 Shape 等相關類別放入子套件後 , 參考這些類別 時所用的『完整名稱』就必須包含子套件名稱 , 例如 flag.math.Circle, 請參考以下範例。
  49. 49. 在套件中建立子套件  主要的變化就只是在使用子套件類別時 , 都改用 『 flag.math. 類別名稱』的形式。
  50. 50. 在套件中建立子套件  如果有必要再對子套件中的類別和介面加以分類 , 仍 可在子套件中再建立下層的子套件 , 只要在 package 敘述中使用 . 連接完整子套件的名稱即可。  相對的 , 我們也必須自行手動、或是使用編譯器的 -d 選項建立對應的子套件資料夾 , 並且將編譯好 的 .class 檔案放到正確的資料夾中。  在執行的時候 , 只需指定最上層套件所在的位置即可 。
  51. 51. 13-3-2 使用 import 敘述  如果子套件結構較多層時 , 每次要使用一長串完整名 稱來標識類別或介面 , 不管是編寫或閱讀都會感到不 便。  此時 , 如果我們已確定自己程式中的類別名稱都不會 與套件中的類別名稱相衝突 , 即可利用 import 敘述 , 改善程式總是出現一長串名稱的情形。
  52. 52. 使用 import 敘述  import 敘述的功用 , 是將指定的套件類別、或套件 名稱『匯入』目前程式的名稱空間中 , 此後要使用該 類別或該套件中的類別時 , 即可單純以其類別名稱來 識別 , 而不需冠上套件名稱 , 簡化程式的撰寫。  使用 import 敘述的方式可分為兩種方式:匯入單一 類別、或匯入套件。
  53. 53. 匯入指定套件中的單一類別  import 敘述最簡單的使用方式 , 就是直接匯入指定 套件中的單一個類別 , 例如:
  54. 54. 匯入指定套件中的單一類別  程式第 1 行就是用 import 敘述匯入 flag.math 套 件中的 Rectangle 類別的名稱 , 所以之後直接用 Rectangle 這個名稱即可使用該類別 , 但使用同一套 件中的其它類別時 , 則仍是要撰寫完整名稱。
  55. 55. 匯入指定套件中所有的類別名稱  如果程式同時要用到同一套件中的多個類別 , 並不需 撰寫多行 import 敘述一一匯入所要用到的類別名稱 , 而可用萬用字元 *, 表示要匯入指定套件中所有的類 別名稱。  例如 , 上一個程式可改成。
  56. 56. 匯入指定套件中所有的類別名稱
  57. 57. 匯入指定套件中所有的類別名稱  由於第 1 行用 "import flag.math.*" 敘述匯入 flag.math 套件中所有的類別 , 因此在程式中使用該 套件的類別時 , 就不再需要使用完整名稱 , 亦可參考 到套件中的類別。  但要特別注意 , 萬用字元只代表該套件下的所有類別 , 而不包含其下的子套件。舉例來說 , 如果將 ImportPackage.java 的第一行改成:
  58. 58. 匯入指定套件中所有的類別名稱  表示只匯入 flag.* 類別或介面 , 但未匯入 flag.math 子套件下的類別或介面 , 因此編譯到第 5 、 6 行時 就會發生錯誤 , 因為事實上未匯入子套件 flag.math 中的類別名稱 , 所以編譯器就不知道第 5 、 6 行的 Rectangle 、 Circle 所指為何。 ▪ 當 package 和 import 都有時 , package 必須寫在最 前面 , 然後是 import, 再來才是 class 的定義。請務 必記住這個順序。
  59. 59. 13-3-3 使用 import static 敘述  前面介紹 import 可匯入指定套件中的某個類別 ( 或 所有類別 ) 的名稱 , 以便在程式中可以少打一些字 ( 只打類別名稱 , 而省略套件名稱 ) 。  如果類別中也有靜態成員 ( 變數或方法 ), 那麼還可 用 import static ( 靜態匯入 ) 來再少打一些字 ( 只打 成員名稱 ), 例如:
  60. 60. 使用 import static 敘述  由於 out 是 System 類別的靜態成員 , 而 toHexString() 、 MAX_VALUE 也是 Integer 類別的 靜態成員 , 因此可以將程式改寫如下 ( 以上粗體的字 都可省略 ) :
  61. 61. 使用 import static 敘述  這樣是不是省事多了呢!不過在省事之餘 , 卻也降低 了程式的可讀性 ( 例如 MAX_VALUE 倒底是哪裡冒 出來的? ), 所以是否要使用這項功能 , 就留給讀者 自行決定了。  另外有一點要注意 , 如果同時 import 了多個套件 , 那麼不同套件之間若有名稱相同的類別 , 則會造成編 譯錯誤。
  62. 62. 使用 import static 敘述  同理 , 若使用 import static 匯入了多個類別 , 那麼 也有可能發生靜態成員名稱重複的狀況 , 例如:  由於 Integer 和 Double 中都有 MAX_VALUE 靜 態常數 , 因此在程式中使用到 MAX_VALUE 時就會 編譯錯誤。
  63. 63. 13-3- 4 套件與存取控制的關係  在前面幾章中討論過存取控制字符與繼承的關係 , 其 中 protected 存取控制字符和套件息息相關 , 我們在 這裡做進一步的討論。  先回顧一下第 9 章所提過的存取控制字符。
  64. 64. 套件與存取控制的關係 ▪ 最外層的類別只能設為 public 或都不設 ( 預設存取控 制 ), 千萬不可設為 protected 或 private, 而且也不可 設為 static, 否則會編譯錯誤。但內部類別則不受這些 限制。
  65. 65. 套件與存取控制的關係  由於存取控制字符可以加諸在成員、方法或是類別上 , 因此必須特別小心。  舉例來說 , 若類別的某個成員變數是 public, 但類別 本身是預設存取控制 , 則套件外部根本無法使用它 , 自然也無法存取其 public 的成員變數 , 請參考以下 範例:
  66. 66. 套件與存取控制的關係  DefaultClass 並未加上任何存取控制字符 , 因此只有 同一套件中的類別才能使用 , 即使成員變數 i 是 public, 套件外的程式也無法存取。 ▪ 在上一章中提到過 , 介面預設就是 public, 所以不會 有如上的問題。
  67. 67. 套件與存取控制的關係  為了讓程式的用途明確清楚 , 建議應為所有的類別以 及成員、方法標示合適的存取控制字符 , 基本的使用 規範如下: ▪ 除非類別是要提供給套件外的其他類別使用 , 否則請 不要標示為 public 。 ▪ 如果成員或是方法只是提供給同一套件的其他類別使 用 , 而且允許衍生類別重新定義或是修改內容的話 , 請將之標示為 protected 。
  68. 68. 套件與存取控制的關係 ▪ 如果成員或是方法只是提供給同一套件的其他類別使 用 , 就不要標示存取控制字符 , 採用預設存取控制。 ▪ 如果成員或是方法只是提供給類別內的其他成員或是 方法使用 , 就請標示為 private 。
  69. 69. 13-3-5 預設套件 (Default Package)  還記得在 13-1 節中 , 我們將幾何圖形的類別都單獨 存放在單獨的檔案中 , 回頭看一下各檔案的內容 , 您 可能會覺得奇怪:這些類別都沒有包裝在同一個套件 中 , 而且它們也都沒有加上存取控制字符。  依據上一小節的說明 , 這些類別應該都只能給同一套 件中的類別存取 , 為什麼在主程式的 UsingOtherClasses 類別中可以直接使用這些類別 , 而不會出現錯誤呢?
  70. 70. 預設套件 (Default Package)  這是因為對目前資料夾中所有未標示套件的類別 , Java 都會將之歸為預設套件 (Default Package) , 這個預設套件相當於一個沒有名字的套件。  所以在 UsingOtherClasses.java 程式中所用到同一 資料夾中的其它類別 , 就都屬於同一個預設套件 , 所 以程式可正常使用這些類別。  其實在前面幾章中將這些類別都放在同一個檔案中時 , 也一樣是因為歸屬到同一個預設套件 , 而可以相互 使用。
  71. 71. 13-3-6 Java 標準類別庫  事實上 , Java 預設就提供了許多的套件 , 可以幫助 您處理多種工作。  例如 java.io 套件中就提供了許多輸出輸入的相關類 別。  像是前幾章曾經使用到的 BufferedReader 與 InputStreamReader 就屬於此套件 , 所以有些範例 程式會在開頭 import java.io.* 。
  72. 72. Java 標準類別庫  另外 , java.lang 則提供了許多與 Java 語言本身有 關的類別 , 像是對應於基礎型別的 Integer 等類別 , 就屬於此一套件。不過由於 Java 預設就會匯入 java.lang.*, 所以我們不需在程式中自行匯入。  從本書一開始就使用到的 System 就是 java.lang 套件中的類別 , 它有 out 這個 static 的成員 , 指向 一個 java.io.PrintStream 物件 , 因此我們才能呼叫 System.out.println( ) 方法將資料顯示在螢幕上。  有關 Java 本身提供的各類套件 , 統稱為 Java 標 準類別庫 , 會在第 17 章做進一步的介紹
  73. 73. 13- 4 套件的命名與打包  雖然套件可以防範類別名稱重複的問題 , 但套件本身 的名稱也可能會重複 , 因此本節將介紹套件的命名慣 例 , 只要大家遵循此慣例 , 就可避免名稱重複的問題 。  另外 , 我們寫好的程式或套件 , 也可將其內容 ( 路徑 結構及類別檔 ) 壓縮成 JAR 檔 , 以方便傳遞或安裝 使用;而其好處 , 是在使用時可直接存取其內容 , 就 好像沒有壓縮一樣。
  74. 74. 13-4-1 套件的命名慣例  雖然使用套件已經可以減少類別名稱重複的問題 , 不過為了徹底解決這個問題 , 一般來說 , 套件的命 名都會依循以下的規則 , 避免套件名稱也發生衝突 : 1. 每家公司 ( 或是組織 ) 以其在網際網路上的網域名 稱相反的順序為最上層套件的名稱 , 例如旗標出版公 司的網域名稱是 flag.com.tw, 因此 , 只要是旗標所 撰寫的類別 , 都應該要放置在 tw.com.flag 這個套件 中。
  75. 75. 套件的命名慣例 2. 如果需要的話 , 可以再依據部門或是工作單位名稱 , 在最上層套件中建立適當的子套件 , 以放置該部門所 撰寫的所有類別 , 避免同一公司、不同單位的人使用 相同的類別名稱。 3. 在最上層套件中 , 再依據類別的用途建立適當的子套 件。舉例來說 , 前面我們所建立的 flag.math 或是 flag.utility 子套件 , 其實應該要建立為 tw.com.flag.math 以及 tw.com.flag.utility 套件才對 。
  76. 76. 套件的命名慣例  透過這樣的方式 , 就可以確立不同單位所提供的套件 , 其中的類別一定不會有重複的完整名稱。  如此一來 , 即便不同單位的人寫了同樣名稱的類別 , 但是冠上完整套件名稱時 , 這兩個類別就不同名了。
  77. 77. 13-4-2 將程式打包成 JAR 壓縮檔  當我們寫好程式之後 , 可以將所有的類別 ( 含套件的 路徑結構 ) 都包成 JAR 壓縮檔 , 以方便傳遞或安裝 。  JAR (Java Archive) 檔是一種類似 ZIP 格式的壓縮 檔 , 副檔名為 .jar, 我們可使用 Java 提供的 jar.exe 來進行壓縮及解壓縮操作。  在示範 JAR 的操作之前 , 我們先來準備要打包套件 的路徑結構及類別檔 , 請在書附範例的 Ch13 中執 行以下命令:
  78. 78. 將程式打包成 JAR 壓縮檔  此命令會編譯上一節寫好的 math 套件 (4 個形狀類 別 ) 原始檔 , 由於使用了 -d 13-4 參數 , 所以會自 動在 13-4 中建立套件的路徑 flagmath, 以存放編 譯好的 4 個類別檔。  接著就來打包 JAR 檔 , 請移到 13-4 中執行以下 命令: ▪ 直接執行不加參數的 jar 命令 , 即會顯示 jar 的使用 說明。另外 , 參數的 - 也可省略 , 例如 -cf 也可寫成 cf 。
  79. 79. 將程式打包成 JAR 壓縮檔  如此就可建立好 flag.jar 檔了 , 底下列出 jar 檔的內 容:
  80. 80. 將程式打包成 JAR 壓縮檔  在 JAR 檔中會維持套件的路徑結構 , 並自動在 META-INF/ 子資料夾中加入一個 MANIFEST.MF 說明檔 , 此檔可用來記錄版本編號、建立者、是否包 含可執行的類別、以及執行時的 classpath 等資訊 。  在一般狀況下可不用管它 ( 如果要修改 , 則需遵循一 定的格式 ) 。
  81. 81. 讓 JAR 檔 可以直接執行  其實 JAR 檔不僅可用來打包套件 , 甚至還可連要執 行的類別一起打包 , 那麼只要在 Windows 中雙按該 JAR 檔即可執行程式 ( 或執行 java -jar flag.jar 命 令)。  假設我們在 13-4 中編譯好一個可執行的 App.class, 那麼就可用以下指令連 flag 套件一起打 包:
  82. 82. 讓 JAR 檔 可以直接執行  以上 cfe 的 c 表示要建立 JAR 檔 , f 表示接下來 的參數為 JAR 的檔名 , e 則表示再下一個參數為要 執行的類別名稱。  而要加入 JAR 的資料夾 ( 或檔案 ) 則放在最後 , 可 以列出多個 , 例如以上的 flag 資料夾及 App.class 。建好 app.jar 之後 , 就可以執行 java -jar app.jar 來測試了 ( 由於程式只有文字輸出 , 若 在 Windows 中雙按來執行 , 則將看不到輸出的文 字)。
  83. 83. 將程式打包成 JAR 壓縮檔  當我們要使用 JAR 套件時 , 同樣是用 -cp 參數來 指定 JAR 檔即可 , 例如在 Ch1313-3 中執行上一 節的 ImportPackage.java 程式: ▪ 以上 -cp 路徑中的 ".." 是代表上一層資料夾。而執行時的 -cp 參數中 , 也要加上 ".;", 代表也要到目前路徑中尋找所 需的類別檔 (ImportPackage.class 是放在目前路徑中 ) 。
  84. 84. 將程式打包成 JAR 壓縮檔  您可以把 flag.jar 想像成是一個虛擬資料夾 , 因此 jar 檔內、外的路徑可以串接起來 , 例如 134flag.jar 就相當於 13-4flagmath*.class 。  千萬不可將 flag.jar 省略 ( 例如 -cp ..13-4), 否則 Java 只會尋找 13-4flag... 子資料夾 , 而不會自動 尋找 JAR 檔的內容。
  85. 85. 將 JAR 套件加到系統類別庫中  如果打包好的 JAR 套件經常需要使用 , 那麼也可將 之放入 Java 的系統類別庫擴充路徑中: %JAVA_HOME%jrelibext 。  其中的 %JAVA_HOME% 是指 Java 的安裝路徑 , 例如 "C:Program FilesJavajdk1.6.0_11" 。 ▪ 在 SCJP 考試中 , 有時會出現 $JAVA_HOME 、 JAVA_HOME 之類的字眼 , 都是代 表 Java 的安裝路徑。在 Windows 中如果設定了 JAVA_HOME="C:Program..." 環境變數 , 那麼就可 在其他路徑中使用此變數 , 例如 Java 程式檔所在的 路徑可寫成 %JAVA_HOME%bin 。
  86. 86. 將 JAR 套件加到系統類別庫中  如果要測試 , 可將前面建立的 flag.jar 移到系統類別 庫擴充路徑中 , 然後將 Ch1313-3 中的 ImportPackage.java 單獨複製到上層資料夾 ( 以便 與原路徑中的 flag 子資料夾分離 ), 再到 Ch13 中 測試:
  87. 87. 將 JAR 套件加到系統類別庫中  如果可以編譯但無法執行 , 則通常是因為系統另外還 安裝了 JRE (Java Runtime Environment) 版的 Java, 也就是只能執行程式而無編譯功能的版本。  此時還必須將 flag.jar 也複製到 JRE 安裝路徑 ( 例 如 C:Program FilesJavajre6) 的 libext 中才行 , 因為 java.exe 只會到此處尋找系統擴充類別。
  88. 88. 13-4-3 編譯與執行時的參數設定  關於如何編譯與執行 Java 程式 , 相信大家都已經很 熟悉了 , 本節再來補充一些實用的命令列參數。  底下是 javac.exe 常用的幾個參數。
  89. 89. 編譯與執行時的參數設定
  90. 90. 編譯與執行時的參數設定  底下則為 java.exe 的常用參數 , 請注意 -D 參數和 javac.exe 的 -d 參數是不同的。
  91. 91. 編譯與執行時的參數設定
  92. 92. 編譯與執行時的參數設定  其中要特別說明的是 -D 參數。  Java 系統本身維護了一組系統參數 (System Properties), 其中記錄了如作業系統名稱 / 版本、目 前使用者的帳戶、系統的換行符號、暫存檔的路徑 . . . 等資訊。  而我們也可用 -D 參數來新增或修改系統屬性 , 例如 -Dmy.name=Joy 則可新增一個名為 my.name 的 系統屬性 , 其值為 "Joy" 。
  93. 93. 編譯與執行時的參數設定  在程式中 , 則可使用以下方法來取得特定的系統屬性 值:
  94. 94. 編譯與執行時的參數設定  其中 System.getProperti es() 會傳回所有系統屬性 的值 , 因此可再用 .getProperty() 來進一步取得特定 屬性的值 , 其結果與直接使用 System.getProperty() 相同。  若想列出所有的系統屬性 , 則可執行以下程式:
  95. 95.  13-A 加入新的類別到 flag 套件中
  96. 96. 1. Given: Which class can access the variable fs? A. only in ZipFile class. B. any class extends ZipFile class. C. any class under the mytools package. D. any class in the mytools.util package. E. any class that has-a or is-a ZipFile class.
  97. 97. 2. If class A extends class B, and class B is packed in My.jar. Which statements will allow class A compiles? (Choose t hree . ) A. My.jar is located at /mytool/ and a classpath environment variable is set to include /mytool/My.jar.B. B. My.jar is located at /mytool/ and a classpath environment variable is set to include /mytool/My.jar. C. My.jar is located at /mytool/ and a classpath environment variable is set to include /mytool/My.jar.B.class.
  98. 98. D. My.jar is located at /mytool/ and the A class is compiled using javac -classpath /mytool/ A.java. E. My.jar is located at /mytool/ and the A class is compiled using javac -cp /mytool/My.jar A.java. F. My.jar is located at $JAVA_HOME/jre/lib/ext/. G. My.jar is located at $JAVA_HOME/jre/bin/. H. My.jar is located at $JAVA_HOME/jre/classes/.
  99. 99. 3. Which code, inserted at line 3, will output "scjp" when excute "java -Dmy.pwd=scjp X"? A. args[0].substring(args[0].length()-4) B. B. System.getEnv("my.pwd") C. System.getProperty("my.pwd") D. System.getProperties("my.pwd") E. System.getProperties().getProperty("my.pwd")
  100. 100. 4. The image represents the stucture of my package. Please place the codes to the empty box that make Find class compiles .
  101. 101. 5. Given: Which code inserted at line 4 in Drive class will compile? A. move(2);    B. Car.move(2);    C. tool.Car.move(2); D. import tool.Car; Car.move(2); E. Drive can't use the method in Car. F. super.tool.Car.move(2);
  102. 102. 6. Given two files: Which code inserted at l ine 1 in second file will compile? A. import tool.*; B. import tool.Car.*; C. import static tool.*; D. import static tool.Car.move; E. import static tool.Car.move(); F. static import tool.*; G. static import tool.Car.*;
  103. 103. 7. Given: This App.class locates in /work/my/image/apps and the CLASSPATH environment variable is set to " . " . Which two command lines will run App? (Choose all that apply. )
  104. 104. A. java App (if run from /work/). B. java App (if run from /work/my/image/apps/). C. java my.image.apps.App (if run from /work/). D. java my.image.apps.App (if run from /work/my/image/apps/). E. java -cp /work my.image.apps.App (if run from any directory). F. java -cp . App (if run from /work/my/image/apps/). G. java -cp /work/my/image/apps;. App (if run from /work/).

×