Java的Package和Classpath

Package

在Java中,Package是用來包含一系相關實例的集合。這些相關聯的實例包括:類、接口、異常、錯誤以及枚舉。html

Package主要有一些的幾點做用:java

  1. Package能夠處理名字衝突,在衝突的名字前加上包的名字,經過使用名字的全限定名來訪問名字的時候,能夠避免名字衝突。由於在不一樣的包之間,具備不一樣的包名,因此能夠經過全限定名來區分不一樣包中同名的名字。Package的這種機制稱爲名字空間管理(Namespace Management)。bootstrap

  2. Package能夠實現訪問控制,在Java中,除了經常使用的publicprivate這兩個訪問控制修飾符外,還包含了protecteddefault這兩個訪問控制修飾符,這兩個修飾符都和Package相關。經過protected修飾的實體,它的訪問受限於同一個包和它的子類中。若是一個實體沒有包含任何的訪問控制修飾符,那麼默認就是default,它的訪問受限於同一個包中。bash

  3. 用於發佈可重用的類的集合,一般會將這些類打包成JAR包的形式。oracle

Package的名字約定

一個包的名字能夠經過將互聯網的域名反向後加上本身的項目名字產生。中間經過.進行分隔。Package的名字採用小寫的方式。(i.e. 若是一我的的域名是'abc.com',那麼他能夠將本身項目的包名寫成 'com.abc.project')。ide

咱們可能會看到在Java官方提供的包中,包含了javajavax前綴的包名,這兩個前綴分別用於官方提供的java包和java的擴展包。工具

包名和目錄結構的關係

Java中的包名和文件系統的目錄結構之間是有聯繫的。同一個包中的實體,都被存儲在同一個目錄下,確切的說,這些實體被存儲在經過包名肯定的子目錄結構下。i.e.,假設存在一個包名爲 com.abc.project的包,那麼這個包中的實體被存儲在目錄$BASE_DIR/com/abc/project下,其中的$BASE_DIR表示了包的根目錄,也能夠稱爲Java中的類路徑。經過上面的例子,咱們能夠發現,將包名中的.轉換爲/就是相應的子目錄結構了。開發工具

上面提到的$BASE_DIR能夠存在於文件系統的任何位置,因此Java的編譯器和虛擬機必須知道$BASE_DIR的位置,才能夠定位到須要的實體。對這個位置的查找是經過環境變量CLASSPATH來實現的。CLASSPATH是一個相似於PATH的環境變量,只是CLASSPATH是用於查找Java的類的位置的。優化

在Java中,沒有子包的概念。i.e.,兩個包java.awtjava.awt.event,這兩個包具備相同的前綴。其中包java.awt中的實體存儲在路徑$BASE_DIR\java\awt下,而包java.awt.event中的實體存儲在路徑$BASE_DIR\java\awt\event下,這是兩個獨立的包,只是具備相同的前綴而已,因此java.awt.event不是java.awt的子包。ui

例子

package com.yyy;
public class Circle {
   private double radius;
   public Circle(double radius) {
      this.radius = radius;
   }
   public double getRadius() {
      return radius;
   }
   public void setRadius(double radius) {
      this.radius = radius;
   }
}

咱們在./src/java/com/yyy目錄下建立了一個com.yyy的包,而且在包中建立了一個Circle類。而後咱們須要將這個類的編譯後的class文件存儲在目錄classes目錄下。

javac -d ./classes ./src/java/com/yyy/Circle.java

javac命令會編譯這個java文件,而後將編譯後的class文件存儲在classes/com/yyy目錄下,com/yyy這個子目錄會根據包名com.yyy自動生成。在這裏,咱們經過選項-d來指定生成的類存放的位置的根目錄。

下面,咱們使用上面建立的類對象:

import com.yyy.Circle;
public class TestCircle {
   public static void main(String[] args) {
      Circle c = new Circle(1.23);
      System.out.println(c.getRadius());
   }
}

咱們在目錄./test下建立類TestCircle,可是咱們不能直接編譯這個類:

cd test
javac TestCircle.java

上面的命令會報錯,提示找不到類com.yyy.Circle,因此咱們須要告訴編譯器該類的位置。經過選項-cp(-classpath)能夠指定類路徑的位置(這個路徑是包所在的根目錄,而不是包中類的目錄。經過這個根目錄,java會自動查找包中的類)。

javac -cp ./classes TestCircle.java

經過-cp指定類路徑之後,能夠順利編譯經過了,可是若是咱們直接執行生成的class文件,仍是會出現問題:

java TestCircle

上面的命令會報錯,提示找不到類com.yyy.Circle,因此咱們須要告訴命令哪裏能夠找到這個類,跟上面同樣,給出這個類所在的位置,也就是類路徑:

java -cp ./classses TestCircle

上面的命令,看似沒什麼問題了,可是運行仍是會報錯,可是此次提示找不到TestCircle類。這是由於java的CLASSPATH若是沒有被顯式定義指定,那麼默認值是當前目錄,因此第一次沒有報找不到TestCircle的錯誤。可是若是咱們顯式更改CLASSPATH的值(經過上面的-cp選項),那麼就默認不會包含當前的目錄,若是須要包含當前的目錄,則須要顯式指定。在CLASSPATH中能夠經過:分隔不一樣的路徑。

java -cp .:./classes TestCircle

若是TestCircle.java被定義在包com.abc中,那麼若是咱們在這個包的根目錄下引用這個類,則須要使用這個類的全限定名:

//TestCircle.java和Circle.java的class文件都被放在classes目錄下
javac -d classes -cp ./classes src/com/abc/TestCircle.java

//咱們當前所在的目錄是包的根目錄,因此引用TestCircle的時候,須要使用全限定名
java -cp ./classes com.abc.TestCircle

CLASSPATH

CLASSPATH是一個環境變量,Java虛擬機和編譯器會經過這個環境變量來查找java類和包的位置。

Java虛擬機查找類的方式

在討論Java的CLASSPATH環境變量以前,先介紹下Java虛擬機是如何查找Class文件的。

java虛擬機,也就是java命令,經過如下的順序查找和加載java的class文件:

  1. Bootstrap classes - 構成java平臺的class文件,包括rt.jar和一些其餘的重要的jar文件
  2. Extension classes - java的擴展機制的class文件,這些class文件以jar的方式組織並存在於擴展目錄中
  3. User classes - 開發者和第三方建立的class文件,這些class文件的位置經過-classpath(-cp)選項來指定,或者經過設置環境變量CLASSPATH來指定。

Java虛擬機查找Bootstrap classes的方式

Bootstrap classes是一些用於實現Java 2平臺的class文件。Bootstrap classes包含在rt.jar和一些其餘的jar文件中,這些jar文件放在jre/lib目錄下。這些包是經過bootstrap class路徑指定的,這個路徑值存儲在sun.boot.class.path這個系統屬性中,這個系統屬性應該是隻讀的,不該該直接修改。

通常狀況下,bootstrap classes路徑是不該該被從新修改的。Java的非標準選項-Xbootclasspath,容許修改這個路徑值來進行自定義定製核心class。

須要指出的是,實現Java 2 SDK的工具class文件並不和bootstrap class文件存放在一塊兒。這些工具sdk存放在目錄/lib/tools.jar下。開發工具會在啓動Java虛擬機的時候將這個jar包添加到user class路徑中。然而,這個加強的user class路徑只是用於執行這些工具,對於處理源代碼的工具,如javacjavadoc,它們使用的是原始的class路徑,而不是這個加強版本的class路徑。

Java虛擬機查找Extension classes的方式

Extension classes是一些用於擴展Java平臺的class文件。這些用於擴展Java平臺的class文件被組織成jar包的形式,存儲在jre/lib/ext目錄下。在這個目錄下的jar文件會經過Java Extension Framework進行加載。在這個目錄下的鬆散的class文件不會被查找,這些class文件必須是以jar/zip包的形式打包之後才能夠被查找。這個擴展包存放的目錄的位置是不能被修改的。

在目錄jre/lib/ext目錄下若是存在多個jar文件,而且這些jar包中包含了同名的class文件,如:

smart-extension1_0.jar contains class smart.extension.Smart
smart-extension1_1.jar contains class smart.extension.Smart

那麼,具體加載上面哪一個smart.extension.Smart類是未定義的。

Java虛擬機查找User classes的方式

Uesr Classes是一些在java平臺上建立的class文。Java虛擬機經過引用 user class path 來查找這些class文件的位置, user class path 是一個包含了class文件的目錄、jar包和zip包的路徑列表。

一個class文件擁有一個反映類的全限定名的子路徑名,如:假設有一個名爲com.mypackage.MyClass的類,而且存放在目錄/myClasses下,那麼目錄myClasses必須在 user class path 中,而且MyClass這個類的路徑必須是/myClasses/com/mypackage/MyClass.class。若是這個類被存儲在myclasses.jar這個jar包中,那麼myclasses.jar這個包必須在 user class path 中,而且這個類在jar包中的路徑必須是com/mypackage/MyClass.class

用戶類路徑,也就是 user class path ,是以字符串的形式指定的,在Unix下經過:進行分隔,而Win下使用;進行分隔。Java虛擬機會將用戶類路徑中的字符串添加到java.class.path系統屬性中,這個屬性的值有幾種設置途徑:

  • 默認值是 .,表示當前目錄
  • 環境變量CLASSPATH的值,這個值會覆蓋默認值。
  • 在命令行中經過-cp / -classpath選項指定的路徑值,這個值會覆蓋默認值和CLASSPATH環境變量的值。
  • 在命令行中經過-jar選項指定的jar包的路徑值,這個值會覆蓋上面全部的值,若是這個選項被設置,那麼全部的用戶類必須經過jar包的形式指定。

經過上面列出的規則,咱們能夠知道,CLASSPATH環境變量的值只是Java查找類的一中方式。

Java虛擬機查找jar包中的類的方式

一個jar包文件一般包含一個稱爲manifest的文件,這個文件包含了這個jar包中的內容。這個manifest文件能夠定義一個稱爲JAR-class-path的類路徑,這個類路徑能夠用於擴展Java的類路徑(前提是這個jar包被加載進來)。經過JAR-class-path訪問類的順序定義以下:

  • 通常狀況下,在一個jar包中的全部class文件都是經過一個JAR-class-path入口引用的,在查找的時候也是以JAR-class-path入口在類路徑中的前後順序進行訪問的。
  • 若是JAR-class-path指向的jar包文件已經被查找過,那麼這個jar包文件將不會被再次查找(這種優化能夠提升效率,而且避免循環查找)。
  • 若是一個jar包文件是做爲一個擴展包安裝的,那麼這個jar包定義的JAR-class-path會被忽略。全部的在這個jar包中的類文件會被認爲是SDK的一部分,或者已經被做爲擴展包安裝。

引用

http://docs.oracle.com/javase/7/docs/technotes/tools/findingclasses.html
http://www.ntu.edu.sg/home/ehchua/programming/java/J9c_PackageClasspath.html

相關文章
相關標籤/搜索