編程學習筆記之Java相關:包的概念和使用

      類似操作系統中的文件目錄結構,Java也利用包來組織和管理類,一個包中可以包含着一些圍繞某個主題的類。 包中還可以有子包,這就形成了包的層次結構。

      對於類來說,類中所有成員的名字都是互不相干的,比如類A與類B中都有一個方法fun,但其實這兩個fun沒有任何關係。在一個Java程序的運行過程中,很可能會從Internet上下載一些類,而用戶並不知曉。所以在Java中需要名字空間的完全控制,用以保證類名的唯一性。而包機制就爲我們的這種目的提供了一種可能性:作爲包的設計人員,可以利用包來劃分名字空間以避免類名衝突,換言之,一個包中的類不能重名,但不同包中的類可以重名!。

      包是一種鬆散的類的集合,一般不要求處於同一個包的類有明顯的相互關係,如繼承、包含等。由於同一個包中的類在默認情況下可以互相訪問,所以爲了方便管理,通常把一些在一起協同工作的類放在同一個包中。系統也是通過反映不同主題的包爲用戶提供不同用途的類,如輸入輸出類、數學運算類、Applet類等。。利用包來管理類,可以實現類庫的共享和複用,類庫是程序員編寫Java程序的寶貴資源,是可以直接拿來使用的“程序芯片”。

      包分爲無名包和有名包,無名包的好處是無需對包作任何聲明,可以拿來方便快捷使用,但也因爲無名包沒有名字,所以其它程序無法訪問無名包中的類,所以說無名包只適合在局部應用。而有名包就可以被其它程序所訪問,可以通過“包名.類名”訪問指定包中指定的類。

      在Java中,一個Java源文件被稱作一個編譯單元,Java的編譯單元具有如下的特性:首先,一個編譯單元中只能有一個public類,而該類的類名必須與該編譯單元的原文件名相同,該編譯單元中的其它類都是該public類的支撐類。其次,經過編譯後,編譯單元中的每個類都會產生一個後綴爲“.class”的字節碼文件,也稱爲類文件。最後,由一個編譯單元所生成的所有類文件,肯定要歸於一個包中,要麼是無名包,要麼是有名包。



2)包的使用

      使用包【創建有名包】的關鍵字是package,而且package語句只能作爲該Java源文件的第一條語句,而且是整個編譯單元中的唯一一條package語句。該語句指定該編譯單元中定義的類屬於哪個包,如果默認該語句,則默認爲無名包。package的使用格式如下:

package 包名; // 包名一般都用小寫字母

      下面我們給出一個較爲具體的包使用例子:在D200_Card.Java源文件中,定義了PhoneCard類、子類NumberCard以及子子類D200_Card,在源文件的第一行用package聲明瞭一個包,包名是cardclasses,所以該編譯單元中的3個類都屬於cardclasses。源文件內容如下:

package cardclasses;

abstract class PhoneCard{
  double balance;
}

abstract class Number_PhoneCard extends PhoneCard{
  long cardNumber;
}

public class D200_Card extends Number_PhoneCard{
  public D200_Card(long cn, double b){
  cardNumber = cn;
  balance = b;
  }

  public String performDial()
  {
    balance -= 0.5;
    return "卡號:"+cardNumber+"\n餘額:"+balance;
  }
}
       根據包與目錄的對應關係,cardclasses包對應的目錄名應該是cardclasses,那麼該目錄該如何創建呢?而上面的編譯單元生成的類文件又該如何存放呢?

      首先我們要確定包的位置,如果我們要把cardclasses放在d:\ temp下,那麼下面有兩種創建包的方法:

第一種,手工創建;

      首先,在Java項目開發中,應該將源文件與類文件分別放在不同的目錄中,因此我們在d:\ temp目錄下又新建了一個d200文件夾,用它來專門存放D200_Card.java源文件,然後在該目錄下編譯源文件【 D:\temp\d200> javac D200_Card.java】,執行該命令後,會在源文件所在的目錄,即d200下生成3個類文件,分別是PhoneCard.class、Number_PhoneCard.class和D200_Card.class。

      然後,按照我們當初對包的設計要求,需要在d:\ temp目錄下創建一個與包對應的cardclasses文件夾,然後將第一步生成的3個類文件都移動到cardclasses目錄下,這就相當於把類添加到包中,因爲用package語句聲明本編譯單元中的類屬於指定的包後,就必須把這些類文件放在這個包中,並且只能通過該包來訪問這些類。


第二種,自動創建;

      這是一種比較方便快捷的方法,它可以讓編譯程序在完成編譯的同時,也能實現包的創建和類的塞入,該功能的編譯命令如下:【D:\temp\d200>javac -d d:\temp D200_Card.java】,我們看到,與前面那個手動方式相比,這一次命令裏面多了一個“-d”選項,在它後面就是指定創建包的位置,這個javac命令會根據源文件中package聲明的包名,在對應的制定目錄下自動創建一個與包名一樣的目錄,並將編譯源文件後所生成的類文件全部放在這個文件夾中,完成包的創建和類的加入。當然,如果對應的包已經存在,那麼javac命令會跳過創建包目錄步驟,而直接將生成的字節碼文件放在該已經存在的包中。如果在這個包存在一個重名的類文件,那麼程序會用新的類文件替換掉已經存在的舊的類文件。最後要說明的是,如果想在特定的當前目錄下創建包,那麼需要使用如下命令:【D:\temp javac -d . d200\D200_Card.java】,其中裏面的“.”意思就是當前目錄。


      最後介紹一下嵌套包的創建,以我們上面的代碼爲例,如果我們的package是如下使用:

package cardsystem.cardclasses; //這就是嵌套包
那麼編譯該源文件後會創建兩個包,首先是cardsystem,然後在它裏面再創建一個cardclasses。



3)包的使用

      將類組織成包的目的是爲了更好的使用這些類,一個類要用到其它的類,無非是訪問其它類的靜態成員、繼承這個類或創建這個類的對象並訪問這個對象的屬性和方法。

      針對前面我們創建的cardclasses包和裏面的三個類文件,我們將在這裏討論如何在包的外部訪問它們。對於同一個包中的其它類,直接通過類名就可以訪問,而如果是要訪問其它包中public類,就需要指定被訪問類所屬的包了。

      我們首先在d:\temp下建立一個測試程序:

public class TestPackage{
  public static void main(String args[])
  {
    D200_Card my200 = new D200_Card(11111111, 50);
    System.out.println(my200.performDial());
  }
}
但是上面的代碼連編譯都無法通過:“找不到D200_Card類”,這是因爲D200_Card類文件並沒有在當前的無名包中,程序也沒有指明這個類文件在什麼地方,所以編譯會報錯,改成如下形式即可:

public class TestPackage{
  public static void main(String args[])
  {
   cardclasses.D200_Card my200 = new cardclasses.D200_Card(11111111, 50);
    System.out.println(my200.performDial());
  }
}
雖然這種方法是可行的,但實際應用時卻很麻煩,因爲我們要在每次使用D200_Card類時都要在前面加上cardclasses,這樣非常不方便。所以流行的解決方式是使用下面這種方法:

import cardclasses.D200_Card; //這樣就引入了需要使用的D200_Card類

public class TestPackage{
  public static void main(String args[])
  {
    D200_Card my200 = new D200_Card(11111111, 50);
    System.out.println(my200.performDial());
  }
}
甚至我們可以直接引入整個包:

import cardclasses.*; //引入了整個包,“*”意味着包中所有的類

public class TestPackage{
  public static void main(String args[])
  {
    D200_Card my200 = new D200_Card(11111111, 50);
    System.out.println(my200.performDial());
  }
}
需要注意的是,在一個編譯單元中,import不限於一個,但它們必須要出現在所有的類定義之前!此外,import語句僅僅是將指定包中的類引入到當前的類名空間,就是告訴編譯程序去哪兒找程序中要使用的類,import語句並不加載類的定義,也不會對那些類文件產生不好的影響。

      當引入的不同包中有類重名的情況時,爲區分使用的是哪個包中的類,在類名前加上包名最前綴。在import語句中,包名部分也可以包含小數點,如【import Java.awt.*;】,這句話的意思是:引進Java包中的awt子包中的所有類。


      最後還要講一下包的定位,包放在哪個目錄下可以由程序員自由指定,雖然在程序中可以通過多種方法指定包的位置,但系統又該到什麼地方查找這個包呢?這就牽扯到環境變量CLASSPATH了。

      CLASSPATH就像操作系統中的PATH,它指明瞭查找包的路徑,系統將環境變量CLASSPATH包含的一個或多個目錄作爲起始目錄來查找包目錄,並在找到的包目錄下查找類文件。

      例如我們可以在命令方式下這樣設置CLASSPATH環境變量:【 set CLASSPATH = .;d:\temp 】,這樣將會爲系統設置了兩個查找包的途徑:第一是查看當前目錄下是否有程序需要的包,第二是查看【d:\temp】目錄下是否有程序需要的包。




發佈了53 篇原創文章 · 獲贊 113 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章