Java 包(Package)是用来防止命名冲突,控制访问权限,以及让类、接口、枚举和注解等类型的查找和使用更加方便。简单来说,Java 包可以定义为相关类型(类、接口、枚举和注解)的分组,提供访问保护和命名空间管理。
Java 包有两种类型:
-
内置 Java 包:这些是由 Java 平台提供的标准库,比如
java.lang
,java.io
等。
-
用户定义的 Java 包:用户可以根据需要创建自己的包来组织相关的类或接口。
一些现有的 Java 包包括:
用户定义的 Java 包
你可以定义自己的包来捆绑类或接口的集合。将相关的类分组在一起是一个好习惯,这样程序员可以很容易地确定这些类、接口、枚举和注解是相关的。
由于包创建了一个新的命名空间,所以不会与其他包中的名称发生冲突。使用包也更容易提供访问控制,并且也更容易找到相关的类。
创建 Java 包
创建包时,你需要选择一个包名,并在包含你想要包含在包中的类、接口、枚举和注解类型的每个源文件的顶部包含一个包声明。
包声明应该是源文件的第一行。每个源文件中只能有一个包声明,并且它适用于文件中的所有类型。
如果不使用包声明,则类、接口、枚举和注解类型将被放置在当前的默认包中。
编译带包声明的 Java 程序
要编译带有包声明的 Java 程序,你需要使用 -d
选项指定输出目录,如下面的命令所示:
javac -d Destination_folder file_name.java
然后会在指定的目的地创建一个与给定的包名同名的文件夹,并且编译后的类文件会被放置在这个文件夹中。
Java 包示例
下面是一个创建名为 animals
的包的示例。通常建议使用小写字母作为包名,以避免与类和接口的名字发生冲突。
创建包
package animals;
interface Animal {
public void eat();
public void travel();
}
实现接口
现在,让我们在同一包 animals
中实现上面的接口:
package animals;
public class MammalInt implements Animal {
public void eat() {
System.out.println("Mammal eats");
}
public void travel() {
System.out.println("Mammal travels");
}
public int noOfLegs() {
return 0;
}
public static void main(String args[]) {
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}
输出
编译并运行上述代码后,输出将是:
Mammal eats
Mammal travels
导入 Java 包
如果一个类想要使用同一包中的另一个类,那么不需要使用包名。同一包内的类可以直接相互引用,无需特殊的语法。
如果类不在同一个包内,那么可以使用完全限定名,或者使用 import
语句导入整个包或单个类。
示例
假设 Employee
类在 payroll
包内,Boss
类也需要引用 Employee
类,那么 Boss
类可以像下面这样做:
package payroll;
public class Boss {
public void payEmployee(Employee e) {
e.mailCheck();
}
}
如果 Employee
类不在 payroll
包内,那么 Boss
类必须使用以下任一技术来引用不同包中的类:
-
使用完全限定名,例如
payroll.Employee
。
-
使用
import
关键字和通配符 *
导入整个包,例如 import payroll.*
。
-
使用
import
关键字导入单个类,例如 import payroll.Employee
。
示例
package payroll;
public class Employee {
public void mailCheck() {
System.out.println("Pay received.");
}
}
package payroll;
import payroll.Employee;
public class Boss {
public void payEmployee(Employee e) {
e.mailCheck();
}
public static void main(String[] args) {
Boss boss = new Boss();
Employee e = new Employee();
boss.payEmployee(e);
}
}
输出
Pay received.
Java 包的目录结构
当一个类被放置在一个包中时,会产生两个主要的结果:
-
包的名字成为了类名的一部分,正如我们在前一节讨论的那样。
-
这里有一种简单的文件管理方式:
-
将类、接口、枚举或注解类型的源代码放在一个文本文件中,文件名是类型的简单名称,扩展名为
.java
。
例如:
package vehicle;
public class Car {
}
现在,将源文件放在一个反映该类所属包的名称的目录中:
...\vehicle\Car.java
此时,限定的类名和路径名如下:
-
-
路径名 →
vehicle\Car.java
(在 Windows 中)
一般来说,公司会使用其反向的互联网域名作为其包名。
示例
假设一家公司的互联网域名为 apple.com
,那么它的所有包名都会以 com.apple
开头。包名的每一部分都对应一个子目录。
例如,如果该公司有一个名为 com.apple.computers
的包,该包包含一个 Dell.java
源文件,那么它将被包含在一系列子目录中:
...\com\apple\computers\Dell.java
在编译时,编译器为每个在其中定义的类、接口和枚举创建不同的输出文件。输出文件的基础名是类型的名称,扩展名是 .class
。
例如:
package com.apple.computers;
public class Dell {
}
class Ups {
}
现在,使用 -d
选项编译这个文件:
$ javac -d . Dell.java
文件将会被编译为:
.\com\apple\computers\Dell.class
.\com\apple\computers\Ups.class
你可以像下面这样导入 com.apple.computers
中定义的所有类或接口:
import com.apple.computers.*;
与 .java
源文件类似,编译后的 .class
文件也应该位于反映包名的一系列目录中。但是,.class
文件的路径并不一定要与 .java
源文件的路径相同。你可以分别安排你的源文件和类文件目录,如下:
<path-one>\sources\com\apple\computers\Dell.java
<path-two>\classes\com\apple\computers\Dell.class
通过这样做,可以允许其他程序员访问类目录而不泄露你的源代码。你也需要以这种方式管理源文件和类文件,以便编译器和 Java 虚拟机(JVM)能够找到程序使用的所有类型。
类目录的完整路径 <path-two>\classes
称为类路径,并通过系统变量 CLASSPATH
设置。编译器和 JVM 通过将包名添加到类路径来构建到 .class
文件的路径。
假设 <path-two>\classes
是类路径,包名是 com.apple.computers
,那么编译器和 JVM 将在 <path-two>\classes\com\apple\computers
中查找 .class
文件。
类路径可能包含多个路径。多个路径应该用分号(Windows)或冒号(Unix)分隔。默认情况下,编译器和 JVM 会搜索当前目录和包含 Java 平台类的 JAR 文件,因此这些目录会自动包含在类路径中。
设置 CLASSPATH
系统变量
要显示当前的 CLASSPATH
变量,可以在 Windows 和 Unix(Bourne shell)中使用以下命令:
-
在 Windows 中:
C:\> set CLASSPATH
-
在 Unix 中:
% echo $CLASSPATH
要删除 CLASSPATH
变量的当前内容,可以使用:
-
在 Windows 中:
C:\> set CLASSPATH =
-
在 Unix 中:
% unset CLASSPATH; export CLASSPATH
要设置 CLASSPATH
变量:
-
在 Windows 中:
set CLASSPATH = C:\users\jack\java\classes
-
在 Unix 中:
% CLASSPATH = /home/jack/java/classes; export CLASSPATH