原文出自 http://www.cnblogs.com/ggjucheng/archive/2012/12/17/2821935.html
英文出自 http://docs.oracle.com/javase/tutorial/java/package/summary-package.html
包
該章節解說如何捆綁類和介面到包裡,如何使用包裡的類,如何在檔案系統分類,讓編譯器找到你的原始碼。
建立和使用包
為了讓類型更容易尋找和使用,避免命名衝突,存取控制,程式員要把相關的類型的組捆綁為包。
聲明:包是相關的類型的組,提供存取控制,命名空間管理。注意類型是類,介面,枚舉和註解。枚舉和註解類型是特殊的類和介面,所以本章節類型通常稱為類和介面。 Java 平台的一部分的類型是由函數捆綁類的各種軟體包的成員:基本類在java.lang,讀寫類(輸入輸出)在java.io。你也可以放你的類型在包裡。
假設,寫一組表示繪圖物件的類,例如圓形,長方形,線條,點。也可以寫一個介面,Draggable,該類在需要被滑鼠拖拽時被實現。
//in the Draggable.java filepublic interface Draggable { ...}//in the Graphic.java filepublic abstract class Graphic { ...}//in the Circle.java filepublic class Circle extends Graphic implements Draggable { . . .}//in the Rectangle.java filepublic class Rectangle extends Graphic implements Draggable { . . .}//in the Point.java filepublic class Point extends Graphic implements Draggable { . . .}//in the Line.java filepublic class Line extends Graphic implements Draggable { . . .}
捆綁類和介面到一個包力,幾點原因,包括如下:
你和其他程式員可以很容易看出這些類是相關的。
你和其他程式員知道去哪裡找能提供圖形相關方法的類型。
由於包建立了一個新的命名空間,所以你的類型的名字不會和另一個包的類型的名字衝突。
允許同個包的類型,類之間是非嚴格的存取控制,但是對包外的類的訪問嚴格控制。
建立包
要建立一個包,你可以選擇的包的名字(命名規範將在下一節討論),這個名字在上面的每一個源檔案最上面,其中包含的類型(類,介面,枚舉和註解,並把包語句類型)要在包中。
package語句(例如,package graphics;)必須是源碼檔案的第一行。每個源碼檔案只能有一個package語句,並作用於源碼檔案的所有類型。
注意:一個單獨的源碼檔案建立了多個類型,只有一個類可以聲明為public,而且它的名字必須和源碼檔案的名字一樣。例如,在Circle.java檔案中,可以聲明public class Circle,在Draggable.java檔案聲明
public interface Draggable,在Day.java檔案聲明public enum Dy
a,諸如此類。
可以在同樣的檔案中包含一個非pubilc類型(這是強烈不推薦的,除非非public類型是和對應的public類型密切相關,存取範圍更小),但是只有public類型可以在包外訪問。所有的頂層非public類型都是包私人。 如果要把映像介面和類放在先前章節名稱為graphics的包,需要六個源碼檔案,如下:
//in the Draggable.java filepackage graphics;public interface Draggable { . . .}//in the Graphic.java filepackage graphics;public abstract class Graphic { . . .}//in the Circle.java filepackage graphics;public class Circle extends Graphic implements Draggable { . . .}//in the Rectangle.java filepackage graphics;public class Rectangle extends Graphic implements Draggable { . . .}//in the Point.java filepackage graphics;public class Point extends Graphic implements Draggable { . . .}//in the Line.java filepackage graphics;public class Line extends Graphic implements Draggable { . . .}
如果不使用package語句,類型就是在一個匿名包裡。通常來講,匿名包,只是開始部署時,使用的小範圍或者臨時的程式。否則,類和介面要屬於命名的包裡。
命名包
當全世界的程式員使用java程式設計語言,寫類和介面,很可能是,很多程式員為不同的類型命名相同的名字。事實上,之前的例子就是這麼做:它聲明了Rectangle類,而java.awt包也存在一個Rectangle類。但是,如果它們在不同的包裡,編譯器允許兩個類有相同的名字。每個Rectangle類的完全限定名包含包名。這表示,graphics包的Rectangle類的完全限定名是graphics.Rectangle。而java.awt包的Rectangle類的完全限定名是java.awt.Rectangle。
這工作得很好,除非兩個獨立的程式員使用相同的名稱為自己的包。怎麼阻止這個問題呢? 慣例。
命名慣例
包的命名使用小寫,避免類和介面的名稱衝突。
公司使用逆轉的互連網網域名稱作為它們的包名的開始——例如,com.example.mypackage是一個在example.com公司的程式員,建立的包mypackage。
公司內部的命名衝突,由公司的慣例解決,可能是在公司名字後麵包含地區或項目名(例如,com.example.region.mypackage)
java語言本身的包,以java.或者javax.開始。
一些情況下,互連網網域名稱不是一個合法的包名。如果網域名稱包含一個連字號或其他特殊字元,如果名字以數字或者其他字元開始的,是java名字開始的非法使用,或者包名包含java保留關鍵字,例如int。這個情況下,推薦慣例是添加一個底線,例如:
合法化包名
網域名稱 |
包名首碼 |
hyphenated-name.example.org |
org.example.hyphenated_name |
example.int |
int_.example |
123name.example.com |
com.example._123name |
使用包成員
組成包的類型,眾所周知,是包的成員。
從包的外面,使用public的包成員,必須做以下之一:
通過完全限定名引用成員
匯入包成員
匯入成員的完整包
每種適用於不同的情況,如在下面的章節中解釋。
通過完全限定名引用成員
至今,教程中最常見的例子是通過他們的簡明名字引用類,例如Rectangle和StackOfInts。通過簡單名字,引用包成員,可以是代碼寫在包成員的同一個包裡,或者是該成員已經被匯入。
但是,如果你嘗試使用不同包的成員,而這個包沒有被匯入,必須使用成員的完全限定名,這包含了包名。這裡是Rectangle類聲明在graphics包的完全限定名:
graphics.Rectangle
使用完全限定名建立graphics.Rectangle的執行個體:
graphics.Rectangle myRect = new graphics.Rectangle();
完全限定名比較少用。但一個名字重複使用,重複輸入名字,變得單調乏味,並讓代碼變得難以閱讀。可選的是,可以匯入成員或者是它的包,然後使用它的簡單名字。
匯入包成員
要匯入特定成員到當前檔案,import語句,要放在檔案的開始,位於任何型別宣告的前面,但位於package語句的後面,如果有package語句的話。這裡是匯入先前例子graphics包的Rectangle類的例子:
import graphics.Rectangle;
現在可以通過Rectangle類的簡明名字引用它。
Rectangle myRectangle = new Rectangle();
這接近工作的很好,如果你只是使用graphics包的少數成員。但是如果你使用包的很多類型,應該匯入完整的包。
匯入完整包
為了匯入特定包包含的全部類型,使用有加星號(*)的萬用字元的import語句。
import graphics.*;
現在可以簡單名字,引用包graphics的任何類和介面。
Circle myCircle = new Circle();Rectangle myRectangle = new Rectangle();
帶有加星號的import語句,只能用於指定包的所有類,如這裡所示。它不能用於匹配包的部分類。例如,下面的做法,不能匹配graphics包的以A開頭的類。
// does not workimport graphics.A*;
相反,它產生編譯器錯誤。使用import語句,通常只能用於匯入一個包成員或者一個完整包。
注意:不常用的import形式的語句,允許匯入封裝類的public嵌套類。例如,如果graphics.Rectangle類包含有用的嵌套類,例如Rectangle.DoubleWide和Rectangle.Square,通過下面的兩個語句,可以匯入Rectangle和它的嵌套類。
import graphics.Rectangle;import graphics.Rectangle.*;
注意第二條語句不匯入Rectangle。另一種不常用的import,靜態匯入語句,會在後面討論。按照慣例,java編譯器自動為每個源碼檔案匯入兩個完整包:(1)java.lang包和(2)當前包(當前源碼檔案的包)
明顯層次的包
第一,首先,包的出現是分層的,但它們不是。例如,java API包含java.awt包,java.awt.color包,java.awt.font包,還有很多其他的包以java.awt開頭。但是,java.awt.color包,java.awt.font包,和其他java.awt.xxxx包不包含在java.awt包中。首碼java.awt(java抽象視窗工具)用來讓一些相關的包,相關關係更加明顯,但不是顯示包含。
匯入java.awt.*,匯入java.awt包全部的類型,但是它不匯入java.awt.color,java.awt.font,或者其他java.awt.xxx包的任何類。如果你打算像java.awt匯入java.awt.color所有的類型,必須在源碼檔案匯入兩個包:
import java.awt.*;import java.awt.color.*;
名稱歧義
如果一個包的成員和另一個包的成員的名字一樣,而且這兩個包都被匯入,引用每個成員都需要通過他們的完全限定名。例如,graphics聲明了一個類Rectangle,java.awt包也包含了Rectangle。如果graphics和java.awt都被匯入,下面是有歧義的。
Rectangle rect;
這個情況下,必須使用成員的完全限定名,精確指示哪個Rectangle類你要匯入,例如:
graphics.Rectangle rect;
靜態匯入語句
這裡有一個方法,當你需要經常訪問一兩個類的靜態欄位(常量)和靜態方法。這些類的名字作為首碼會經常使用,並導致代碼的混亂。靜態匯入語句,給了一個匯入需要使用的靜態常量和靜態方法讓你不需要使用它們的類名作為首碼。
java.lang.Math類聲明了常量PI和很多靜態方法,包含包括計算正弦,餘弦,正切,平方根,最大值,最小值,指數,和許多其他的方法.例如:
public static final double PI = 3.141592653589793;public static double cos(double a){ ...}
通常情況下,從一個類使用這些對象,需要使用類名作為首碼,如下:
double r = Math.cos(Math.PI * theta);
可以使用靜態匯入語句,匯入java.lang.Math的靜態成員,然後就不用使用該類名為首碼,Math。Math的靜態成員可以獨立匯入:
import static java.lang.Math.PI;
或者是匯入一組:
import static java.lang.Math.*;
一旦他們被匯入,靜態成員的使用不需要限定符。例如,之前的程式碼片段將變成:
double r = cos(PI * theta);
很明顯,寫自己的類時,可以包含自己常用的變數和靜態成員,然後使用靜態匯入語句。例如,
import static mypackage.MyConstants.*;
注意:使用靜態匯入要非常謹慎。過度使用靜態匯入,可能會導致代碼難以閱讀和維護,因為代碼閱讀者,將不知道哪個類定義了一個特定的靜態對象。使用得當,靜態匯入,刪除類的名稱重複,讓代碼更具可讀性。
管理原始碼和類檔案
許多的Java平台的實現依賴於階層的檔案系統來管理原始碼和類檔案,儘管Java語言規範沒有要求。策略如下。
類,介面,枚舉,或注釋類型的原始碼將在一個文字檔,其名稱是簡單的類型名稱,其副檔名是.JAVA。例如:
//in the Rectangle.java file package graphics;public class Rectangle { ... }
然後,原始碼放在一個目錄,目錄名對應類型屬於的包的名稱:
.....\graphics\Rectangle.java
包成員的限定名和檔案的路徑是平行的,假設在Microsoft Windows檔案名稱分隔字元反斜線(在Unix上,用正斜杠)。
- 類名 –
graphics.Rectangle
- 檔案路徑 –
graphics\Rectangle.java
正如你回顧,按照慣例,公司使用它的互連網網域名稱的逆轉作為包名。Example公司,它的互連網網域名稱是example.com,它的所有包名都以com.example開頭,包名的每個元素對應一個子目錄。所以,如果Example公司有一個com.example.graphics包包含一個Rectangle.java源檔案,它將包含類似下面的一系列子目錄:
....\com\example\graphics\Rectangle.java
當編譯一個源檔案,編譯器會為源檔案的每個類型,建立不同的輸出檔案。輸出檔案的基本名稱是類型的名稱,它的擴充民是.class。例如,如果一個源碼檔案類似這樣:
//in the Rectangle.java filepackage com.example.graphics;public class Rectangle { . . . }class Helper{ . . . }
然後被編譯後的檔案會位於:
<path to the parent directory of the output files>\com\example\graphics\Rectangle.class<path to the parent directory of the output files>\com\example\graphics\Helper.class
如.java源檔案,編譯後的.class檔案應該位於,對應包名的一系列的目錄。但是,.class檔案的路徑不必和.java源檔案的路徑一樣,可以獨立配置原始碼和類檔案的目錄,例如:
<path_one>\sources\com\example\graphics\Rectangle.java<path_two>\classes\com\example\graphics\Rectangle.class
這麼做,可以把class目錄給其他程式員,而不需要給原始碼。您還需要以這種方式來管理原始碼和類檔案,使編譯器和Java虛擬機器(JVM)可以找到你的程式使用的所有類型。
classes目錄的完全路徑,<path_two>\classes,稱之為類路徑,由CLASSPATH系統變數設定。編譯器和JVM都會通過添加包名到類路徑,構造路徑到.class檔案。例如,如果
<path_two>\classes
是你的類路徑,包名是
com.example.graphics,
然後編譯器和JVM尋找.class檔案在
<path_two>\classes\com\example\graphics.
類路徑可以包含多個路徑,使用分號(Windows)或冒號(Unix)隔開。預設,編譯器和JVM會尋找目前的目錄和包含Java平台類的JAR檔案,所以這些目錄會自動添加到類路徑。
設定類路徑系統變數
為了顯示當前CLASSPATH變數,在Windows和Unix(Bourne shell)使用下面的命令:
In Windows: C:\> set CLASSPATHIn Unix: % echo $CLASSPATH
刪除CLASSPATH變數的當前內容,使用下面的命令:
In Windows: C:\> set CLASSPATH=In Unix: % unset CLASSPATH; export CLASSPATH
設定CLASSPATH變數,使用下面的變數(例子):
In Windows: C:\> set CLASSPATH=C:\users\george\java\classesIn Unix: % CLASSPATH=/home/george/java/classes; export CLASSPATH
建立和使用包的總結
為類型建立包,package語句要放在類型(類,介面,枚舉,註解)所在的源檔案的第一行.
在不同的包使用public類型,有三個選擇:(1)使用類型的完全限定符(2)匯入類型(3)匯入類型所在的完整包
一個包的源檔案和類檔案的路徑名是包的名稱的鏡像。
需要設定自己的CLASSPATH,然後編譯器和JVM才能根據你的類型找到.class檔案。