原文出自 http://www.cnblogs.com/ggjucheng/archive/2012/12/17/2821935.html
英文出自 http://docs.oracle.com/javase/tutorial/java/package/summary-package.html
包
该章节解说如何捆绑类和接口到包里,如何使用包里的类,如何在文件系统分类,让编译器找到你的源代码。
创建和使用包
为了让类型更容易查找和使用,避免命名冲突,访问控制,程序员要把相关的类型的组捆绑为包。
假设,写一组表示图形对象的类,例如圆形,长方形,线条,点。也可以写一个接口,Draggable,该类在需要被鼠标拖拽时被实现。
//in the Draggable.java file public interface Draggable { ... } //in the Graphic.java file public abstract class Graphic { ... } //in the Circle.java file public class Circle extends Graphic implements Draggable { . . . } //in the Rectangle.java file public class Rectangle extends Graphic implements Draggable { . . . } //in the Point.java file public class Point extends Graphic implements Draggable { . . . } //in the Line.java file public class Line extends Graphic implements Draggable { . . . }
捆绑类和接口到一个包力,几点原因,包括如下:
你和其他程序员可以很容易看出这些类是相关的。
你和其他程序员知道去哪里找能提供图形相关方法的类型。
由于包创建了一个新的命名空间,所以你的类型的名字不会和另一个包的类型的名字冲突。
允许同个包的类型,类之间是非严格的访问控制,但是对包外的类的访问严格控制。
创建包
要创建一个包,你可以选择的包的名字(命名约定将在下一节讨论),这个名字在上面的每一个源文件最上面,其中包含的类型(类,接口,枚举和注解,并把包语句类型)要在包中。
package语句(例如,package graphics;)必须是源码文件的第一行。每个源码文件只能有一个package语句,并作用于源码文件的所有类型。
public interface Draggable,在Day.java文件声明public enum Dy
a,诸如此类。
//in the Draggable.java file package graphics; public interface Draggable { . . . } //in the Graphic.java file package graphics; public abstract class Graphic { . . . } //in the Circle.java file package graphics; public class Circle extends Graphic implements Draggable { . . . } //in the Rectangle.java file package graphics; public class Rectangle extends Graphic implements Draggable { . . . } //in the Point.java file package graphics; public class Point extends Graphic implements Draggable { . . . } //in the Line.java file package 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 work import graphics.A*;
相反,它产生编译器错误。使用import语句,通常只能用于导入一个包成员或者一个完整包。
import graphics.Rectangle; import graphics.Rectangle.*;
明显层次的包
第一,首先,包的出现是分层的,但它们不是。例如,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 file package 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 CLASSPATH In 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\classes In Unix: % CLASSPATH=/home/george/java/classes; export CLASSPATH
创建和使用包的总结
为类型创建包,package语句要放在类型(类,接口,枚举,注解)所在的源文件的第一行.
在不同的包使用public类型,有三个选择:(1)使用类型的完全限定符(2)导入类型(3)导入类型所在的完整包
一个包的源文件和类文件的路径名是包的名称的镜像。
需要设置自己的CLASSPATH,然后编译器和JVM才能根据你的类型找到.class文件。