2018年11月16日 星期五

Java SE 入門教學 - Package部屬

更新時間:11/19/2018
更新時間:11/09/2024

前言

當您專案的類別越寫越多時,需要有一套方式可以管理類別,就像您使用類別管理變數、方法一樣,分門別類。例如在程式中,您也許會定義一個 Rectangle 類別,但另一個與您合作開發程式的開發人員並不曉得已經有這個類別名稱的存在,他可能也定義了一個 Rectangle 類別,於是編譯過後他的 Rectangle 類別檔案會覆蓋您的 Rectangle 類別檔案,問題就發生了。


一、什麼是套件(Package)

套件是 JAVA 用來組織類別、抽象類別、介面與子套件的機制。其作用如下:

  • 防止命名衝突。例如可以有相同類別的名字 Employee 在不同的套件 college.staff.cse.Employee 和 college.staff.ee.Employee 中。
  • 類別(class)、抽象類別(abstract method)、介面(interface)、列舉(enumerations)、註解(annotations)變得更容易搜尋、配置與使用。
  • 存取修飾子發揮作用,可控制訪問權限。
  • 套件可以被視為數據封裝或數據隱藏。

我們必須把相關的類別放在套件中。如此,我們可以簡單地寫上 import 關鍵字導入已經存在的套件包,並在我們的程序中使用它。套件包是一組相關類別的容器,其中一些類別是可訪問的,而其他類別是為內部目的而保留的。我們可以在程序中盡可能地重複使用套件包中已經存在的類別。


二、套件如何運作?

2.1 套件名稱和目錄結構密切相關。

例如,如果套件名為 college.staff.cse,那麼有三個目錄 college、staff 和 cse。cse 在 staff 資料夾內,staff 在 college 資料夾內。所以,可以通過 CLASSPATH 變數訪問 college 資料夾,當然要先在 CLASSPATH 變數添加 college 這個資料夾的父層路徑。

2.2 約定成俗的套件命名規則:

公司如有網域(domain),建議以網域域名(domain name)相反順序命名,例如:com.blogspot.ethan-imagination。
大學裡,推薦使用 college.tech.cse、college.tech.ee、college.art.history 等。

2.3 子套件(Subpackage):

套件內還有另一個套件稱為子套件。默認情況下不會導入它們,必須寫上完整套件與子套件才能導入它們。例如:

import java.util.*;

util 是一個在 java 套件下的子套件。

2.4 package 語法規則:

package 資料夾路徑;

◉ 宣告類別套件所用的 package 敘述必須是除了註解外,第一個出現的敘述。
◉ 假如沒有 package 敘述,類別就歸屬於預設套件(default package)。
◉ 為了避免類別名稱重複出現,最好使用套件將類別加以分類。

2.5 宣告新類別引用外部類別:

import 套件名稱.類別名稱
import 套件名稱.子套件名稱.類別名稱
import 套件名稱.*
import 套件名稱.子套件名稱.*

◉ 外部類別指的是任何與新類別位於不同套件(資料夾)的類別。
◉ 若要引用的類別位於同一個套件則不需要使用 import 宣告,但不同套件則必須使用 import 指令。
◉ 預設 java 在編譯的過程中,會自動引入 java.lang 套件下的類別

▼ 存取修飾子與存取範圍

存取修飾子 同類別 同套件 子類別 全域(不同套件)
private Yes X X X
default Yes Yes X X
protected Yes Yes Yes X
public Yes Yes Yes Yes

2.6 類別的完整宣告:

packge 資料夾路徑;
import 外部類別;

class 類別 {
宣告屬性變數;

建構子;

宣告方法();
}

三、套件的類型

▼ 套件的類型

3.1 內置套件(In-Build Packages)

這些套件由大量類別組成,這些類別是 Java API 的一部分。一些常用的內置套件是:

3.1.1) java.lang:包含語言支持類別(例如,用於定義原始數據類型,數學運算的分類)。此套件將自動導入。
3.1.2) java.io:包含用於支持輸入/輸出操作的類別。
3.1.3) java.util:包含實現鏈接列表,字典和支持等數據結構的實用程序類別; 用於日期/時間操作。
3.1.4) java.applet:包含用於創建 Applet 的類別。
3.1.5) java.awt:包含用於實現圖形用戶界面組件的類別(如按鈕、菜單等)。
3.1.6) java.net:包含用於支持網絡操作的類別。

3.2 自定義套件

這些是自定義的套件。首先,我們在 JavaCode 資料夾下創建一個資料夾為 UserDefinedPackages。 然後在目錄中創建 MyClass,第一個語句是套件名。


▼ 切換資料夾編譯


▼ 此編譯會在當前目錄下
產生 myPackage 資料夾


現在,我們建立另一個類別 PrintName,一樣放在 UserDefinedPackages 資料夾內。


▼ 切換資料夾編譯(當前目錄下編譯)
因為未寫 package,所以為 default package。


四、實作部屬流程

接下來的操作步驟很多,中途會碰到編譯錯誤的狀況,我們利用此範例的狀況把「存取修飾子」的部分,帶出什麼存取修飾子會有怎樣的存取範圍,其影響性是什麼。

4.1 建立一個新的資料夾 shapePackages

c:\JavaCode>mkdir shapePackages
c:\JavaCode>cd shapePackages


4.2 建立第一個類別 Rectangle

package shape.rect;


▼ 切換資料夾編譯(當前目錄下編譯)
產生 shape/rect 兩個資料夾


▼ 在 rect 資料夾裡面
產生一個已經編譯好的檔案 Rectangle.class


4.3 建立第二個類別 Neighbor

package shape.rect;

▼ 切換資料夾編譯(當前目錄下編譯)


▼ 在 rect 資料夾裡面
產生一個已編譯好的檔案 Neighbor.class


4.4 建立第三個類別 Cube

package shape.cube;

▼ 切換資料夾編譯(當前目錄下編譯)
編譯錯誤,找不到符號
無法找尋到 Rectangle 類別


▼ 因為使用到不同套件的類別
所以必須使用 import 指令,導入套件類別


▼ 編譯錯誤
Rectangle 類別不是 public,不能從外部套件被訪問。


▼ 重新修改 Rectangle 類別 的存取修飾子成 public


▼ 先編譯 Rectangle 類別
再編譯 Cube 類別


4.5 寫主程式

package shape.master;

有了前面的經驗,如果新的類別在不同的套件,需要使用關鍵字 import 導入套件類別。
或是直接在創建物件實例時,使用「完全描述」(Fully qualified)打上完整的套件名稱。例如:

  Rectangle robj = new shape.rect.Rectangle(15, 13);

▼ 編譯錯誤
Cube 類別不是 public,不能從外部套件被訪問。
Rectangle 類別內的屬性變數不是 public,不能從外部套件被訪問。


▼ 重新修改 Cube 類別 的存取修飾子成 public


▼ 重新修改 Rectangle 類別內的屬性變數 的存取修飾子成 public



▼ 怎麼還是編譯錯誤?!
原來是 Cube 類別裡的屬性變數 height 權限不夠


▼ 再回去重新修改 Cube 類別內的屬性變數 的存取修飾子成 public


▼ 成功執行了!



4.6 把剛剛寫的程式碼(.java),全部編譯到另一個資料夾下

事實上將原始碼與編譯完成的檔案放在一起並不是一個好的管理方式,您可以建一個專門放原始碼 .java 檔案的目錄,並建一個專門放 .class 檔案的目錄。

javac -d 專門放.class檔案的目錄 專門放原始碼.java檔案的目錄/*.java

「專門放.class檔案的目錄」要先建立起來,我們這邊建立 shapePackages2 資料夾。
「*.java」為附檔名.java的所有檔案。


五、導入(import)套件(package)

如果您有使用 "package" 來為您的類別設定套件管理,則編譯過後 "package" 所設定的名稱就成為類別名稱的一部份,您可以使用「完全描述」(Fully qualified)名稱來指定使用的類別,當然這個方法要打一長串的文字,因而使用上不是很方便,您可以使用 "import" 關鍵字,告知編譯器您所要使用的類別是位於哪一個套件,如此您可以少打一些字,讓編譯器多做一些事。

5.1 導入套件中的特定類別

套件.子套件. ‧‧‧ .類別名稱

例如使用 java 套件中的子套件 util 的 ArrayList 類別

import java.util.ArrayList;

5.2 導入套件中所有的類別

套件.子套件. ‧‧‧ .*

例如使用 java 套件中的子套件 util 的 所有類別

import java.util.*;

5.3 導入套件中類別內的靜態屬性變數

在 J2SE 5.0 後新增了 "import static" 語法,它的作用與 "import" 類似,都是為了讓您可以省一些打字功夫,讓編譯器多作一點事而存在的。

使用 "import static" 語法可以讓您 "import" 類別或介面中的公開的靜態成員(屬性變數和方法),一個實際的例子如下:


範例: import static 與 import 的使用

▼ 紅色的為靜態
  藍色的為非靜態


六、處理名稱衝突

我們唯一需要關注的是當我們發生名稱衝突時。 例如,java.util 和 java.sql 套件都有一個名為 Date 的類別。因此,如果我們在程序中導入兩個套件,如下所示:


編譯器將無法確定我們想要的 Date 類。 使用特定的 import 語句可以解決此問題:


如果我們需要使用兩個不同的 Date 類別,那麼每次宣告該類別的新物件實例時我們都需要使用完整的套件名。
例如:


七、目錄結構

7.1 基目錄(Base Directory)

套件名稱與用於存儲類別的目錄結構密切相關。屬於特定套件的類別(和其他實體)一起存儲在同一目錄中。此外,它們存儲在由其套件名指定的子目錄結構中。例如,shape.rect 套件的類別 Rectangle 存儲為 “$ BASE_DIR \ shape \ rect \ Rectangle.class”,其中 $ BASE_DIR 表示套件的基目錄。顯然,套件名稱中的“點”對應於文件系統的子目錄。

例如 4.6 章節中,$ BASE_DIR 為 "C:\JavaCode\shapePackages2" 目錄。

基目錄($ BASE_DIR)可以位於文件系統的任何位置。因此,必須通知 Java 編譯器和運行時 $ BASE_DIR 的位置以便配置類別。這是通過一個名為 CLASSPATH 的環境變數來完成的。CLASSPATH 類似於另一個環境變數 PATH。

7.2 設置 CLASSPATH

● 設定永久的環境變數。在 Windows 中,
  控制台(control panel) → 系統(system) → 進階系統設定(Advanced)
   → 環境變數(Environment Variable) → 新增系統變數(New system variable)
   → 變數名字使用 CLASSPATH
   → 變數值使用 .;您的.class目錄。例如:

.;c:\javaproject\classes;d:\tomcat\lib\servlet-api.jar

您可以檢查現在配置 CLASSPATH 變數的狀況,指令如下:

> SET CLASSPATH

● 通過發出以下命令,可以為該特定 CMD shell session 臨時設置 CLASSPATH:

> SET CLASSPATH=.;c:\javaproject\classes;d:\tomcat\lib\servlet-api.jar

● 您也可以使用 javac 和 java 命令的命令行選項 -classpath 或 -cp 代替使用 CLASSPATH 環境變數。
例如:

> java -classpath c:\JavaCode\shapePackages2 shape.master.TestRect

八、總結

  • 每個類別都是某些套件的一部分。
  • 如果未指定套件,則類別會被分配到特殊未命名的套件(default package)。
  • 相同的套件包可以放入許多類別。
  • 我們可以使用 "package-name.class-name" 去訪問另一個套件中的公開類別。
  • 使用 "import" 關鍵字,您可以少打一些字,讓編譯器多做一些事。
  • 套件名稱與目錄結構息息相關。




2 則留言:

  1. 2 Typos:
    2.2 ...college 在 staff 資料夾「外」,staff 在 college 資料夾內。...
    2.5 ...與新類別「位」於不同套件...

    回覆刪除
    回覆
    1. 親愛的讀者您好:
      感謝您的反饋,經確認後已校正文章內相關的錯誤。如還需補充,請不吝告知,非常感謝!

      刪除