ITEEDU

入门准备

接触对象

对象导向

资源管理

对象容器(Container)

输入输出(I/O)

执行绪(Thread)

反射(Reflection)

metadata

数据库(JDBC)

舍遗补缺

Java Gossip: 套件(package)

随着程序代码撰写越来越多,程序内容越来越大,您会发现管理程序中的变量名称、方法名称、类别名称也会是一件麻烦的事,尤其是一些同名问题的发生,例如在程 式中,您也许会定义一个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/中相关扩充组件与现行工作目录。