Java Gossip: 套件(package)

来源:互联网 发布:ios视频下载软件 编辑:程序博客网 时间:2024/05/20 10:21

隨著程式碼撰寫越來越多,程式內容越來越大,您會發現管理程式中的變數名稱、方法名稱、類別名稱也會是一件麻煩的事,尤其是一些同名問題的發生,例如在程 式中,您也許會定義一個Point類別代表2維空間的點,也許在程式的某個地方,您也會定義一個 Point類別來表示一個3維空間的點,其它像函式同名、共用變數同名的情況也可能發生,當這種情況發生時,其中一個定義就可能將另一個定義給覆寫掉了。

Java提供package機制,它就像是一個管理容器,可以將您所 定義的名稱區隔管理在package下,而不會有相互衝突的發生,例如您定義了一個 dimension2d與dimension3d的package,在它們之下都有一個Point類別,但由於屬於不同的package,所以這兩個名稱 並不會有所衝突。

Java的package被設計為與檔案系統結構相對應,如果您的package設定是only.caterpillar,則該類別應該在指定目錄(或jar)的onlyfun/caterpillar下可以找到,為了要能建立與package相對應的檔案系統結構,您在編譯時可以加入-d 參數,並指定要建立在哪一個目錄之下。

下面這個程式使用"package"關鍵字來建立package以管理我們所定義的類別: 

  • UsePackage.java
package onlyfun.caterpillar;

public class UsePackage {
public static void main(String[] args) {
System.out.println("Hello! World!");
}
}

在編譯時要使用以下的指令: 
$ javac -d . UsePackage.java 

在編譯時使用 "-d" 參數,並指定在現行目錄 "." 中建立檔案與系統結構,則編譯完成之後,在現行目錄中會出現onlyfun/caterpillar目錄,而當中有一個UsePackage.class 檔案,在編譯完成之後,package的指定就成為class名稱的一部份了,在執行時可以這麼下指令:
$ java onlyfun.caterpillar.UsePackage 
Hello! World! 
 
可以為類別建立package管理,舉下面的例子來說: 

  • Point2D.java
package onlyfun.caterpillar; 

public class Point2D {
private int x, y;

public Point2D() {x = 0; y = 0;}
public Point2D(int x, int y) {this.x = x; this.y = y;}
public int getX() {return x;}
public int getY() {return y;}
}

這個類別建立在Point2D.java檔案中,可以先用以下的指令來編譯它: 
$ javac -d .  Point2D.java

之前說過,package名稱為類別名稱的一部份,除非您重新編譯類別,否則的話無法改變這個名稱,為了要使用這個類別,方法之一是使用完全吻合(Fully qualified)名稱,也就是完整的指出package與類別名稱,例如:

  • UsePackage.java
public class UsePackage { 
public static void main(String[] args) {
onlyfun.caterpillar.Point2D p1 = new
onlyfun.caterpillar.Point2D(10, 20);

System.out.printf("p1: (x, y) = (%d, %d)%n",
p1.getX(), p1.getY());
}
}

當然這個方法在使用上不是很方便,您可以使用"import"關鍵字,告知編譯器要使用的類別是位於哪一個package之下,如此可以少打一些 字,讓編譯器多作一些事,例如
  • UsePackage.java
import onlyfun.caterpillar.Point2D;

public class UsePackage {
public static void main(String[] args) {
Point2D p1 = new Point2D(10, 20);

System.out.printf("p1: (x, y) = (%d, %d)%n",
p1.getX(), p1.getY());
}
}

您在使用"import"時可以指定類別的完整描述,如果您會使用到某個package下的許多類別,您可以使用 '*',表示您可能使用到某個package下的某些類別,再讓編譯器作更多事,例如:
  • UsePackage.java
import onlyfun.caterpillar.*;

public class UsePackage {
public static void main(String[] args) {
Point2D p1 = new Point2D(10, 20);

System.out.printf("p1: (x, y) = (%d, %d)%n",
p1.getX(), p1.getY());
}
}

但要注意的是,如果您import之後,出現類別名稱有同名衝突時,編譯器就不知道如何處理了,例如:
import java.util.Arrays;
import onlyfun.caterpillar.Arrays;
 
public class SomeClass {
    ....
}
 

在這個例子中,編譯器發現有兩個Arrays類別,它不確定若遇到Arrays時您要使用的是java.util.Arrays,或是 onlyfun.caterpillar.Arrays,它只好回報以下訊息:
java.util.Arrays is already defined in a single-type import
import onlyfun.caterpillar.Arrays;
^
1 error

這個時候您就要考慮換一下類別名稱了(如果您有權更動那些類別的話),或者是不使用"import",直接使用完整描述;在"import"時儘量不使用 '*' 也可以減少這種情況發生。

注意如果您提供的類別若不位於相同的package中,您的類別必須宣告為"public",若不宣告則類別預設只能於同一個package中被存取,例如將之前Point2D類別的public拿掉,則編譯UsePackage.java檔案時,會出現以下的錯誤:
UsePackage.java:5: caterpillar.demo.Point2D is not public in caterpillar.demo; cannot be accessed from outside package

如果定義類別成員時沒有指定public、protected或private的存取修飾,則為預設(Default)權 限,成員將只能於同一個package中被直接存取,通常稱之為package-friendly或package-private,且該成員將無法於子 類別中被直接存取。

要存取package的class也與CLASSPATH的設定有關,建議您也看看官方網站上的文章,您對package的瞭解會更深入:
  • Setting the class path (Windows)
  • Setting the class path (Solaris and Linux) 

Java平台的classes是被儲存在Java安裝目錄的jre/lib/下的rt.jar,另外額外的第三元件(Third- party)可以放在/jre/lib/ext/中,在之前的例子中,使用"import"就是在告知編譯器我們的類別位於哪一個package下,這些類別必須設定好CLASSPATH才可以被編譯器找到,預設上是jre/lib/下的rt.jar、jre/lib/ext/中相關擴充元件與現行工作目錄