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

相关文章
相关标签/搜索