类存储在文件系统的子目录中。类的路径必须与包名匹配。
类文件也可以存储在 JAR(Java 归档)文件中。在一个 JAR 文件中,可以包含多个压缩形式的类文件和子目录,这样既可以节省空间又可以改善性能。在程序中用到第三方的库文件时,你通常要得到一个或多个需要包含的 JRE 文件。
为了使类能够被多个程序共享,需要做到下面几点:
- 把类放在一个目录中,例如
/home/user/classdir
。需要注意,这个目录是包树状结构的基目录。如果希望增加
com.xiang117.corejava.Employee
类,那么 Employee 类文件就必须位于子目录/home/user/classdir/com/xiang117/corejava
中。 - 将 JAR 文件放在一个目录中,例如:
/home/user/archives
。 - 设置类路径(class path)。
类路径是所有包含类文件的路径的集合。
在 UNIX 环境中,类路径中的各项之间用冒号(:)分割:
/home/user/calssdir:.:/home/user/archives/archive.jar
而在 Windows 环境中,则以分号(;)分割:
c:\classdir;.;c:\archives\archive.jar
UNIX 和 Windows 都用点号(.)表示当前目录。
类路径包括:
- 基目录 /home/user/classdir 或 c:\classes。
- 当前目录(.)。
- JAR 文件 /home/user/archives/archive.jar 或 c:\archives\archive.jar。
从 Java 6 开始,可以在 JAR 文件目录中指定通配符,
/home/user/classdir:.:/home/user/archives/'*'
或者
c:\calssdir;.;c:\archives\*
在 UNIX 中,* 必须转义以防止 shell 扩展。
archives 目录中的所有 JAR 文件(但不包括 .class 文件)都包含在这个类路径中。
由于总是会搜索 Java API 的类,所以不必显示地包含在类路径中。
警告: javac 编译器总是在当前的目录中查找文件,但 Java 虚拟机仅在类路径中包含 “.” 目录的时候才查看当前目录。如果没有设置类路径,那么没有问题,因为默认的类路径会包含 “.” 目录。如果设置了类路径却忘记包含 “.” 目录,那么尽管你的程序可以没有错误地通过编译,但不能运行。
类路径所列出的目录和归档文件是搜寻类的起始点。下面看一个例子:
/home/user/calssdir:.:/home/user/archives/archive.jar
假定虚拟机要搜寻 com.xiang117.corejava.Employee
类的类文件。它首先要查看 Java API 类。显然,在那里找不到相应的类文件,所以转而查看类路径。然后查找以下文件:
• /home/user/calssdir/com/xiang117/corejava/Employee.class
• com/xiang117/corejava/Employee.class
(从当前目录开始)
• com/xiang117/corejava/Employee.class
(/home/user/archives/archive.jar
中)
编译器查找文件要比虚拟机复杂得多。如果引用了一个类,而没有指定这个类的包,那么编译器将首先查找包含这个类的包。它会查看所有的 import 指令,确定其中是否包含了这个类。例如,假定源文件包含指令:
import java.util.*:
import com.xiang117.corejava.*;
并且源代码引用了 Employee 类。编译器将尝试查找 java.lang.Employee
(因为 java.lang
包总是会默认导入)、java.util.Employee
、com.xiang117.corejava.Employee
和当前包中的 Employee。它会在类路径所有位置中搜索以上各个类。如果找到了一个以上的类,就会产生编译时错误(因为完全限定类名必须是唯一的,所以 import 语句的次序并不重要)。
编译器的任务不止这些,它还要查看源文件(Source files)是否比类文件新。如果是这样的话,那么源文件就被自动地重新编译。在前面已经知道,只可以导入其他包中的公共类。一个源文件只能包含一个公共类,并且文件名与公有类必须匹配。因此,编译器很容易找到公有类所在的源文件。不过,还可以从当前包中导入非公共类。这些类有可能在与类名不同的源文件中定义。如果从当前包中导入一个类,编译器就要搜索当前包中的所有源文件,查看哪个源文件定义了这个类。
1. 设置类路径
最好使用 -classpath (或 -cp,或者 Java 9 中的 --class-path)选项指定类路径:
java -classpath /home/user/classdir:.:/home/user/archives/archive.jar MyProg
或者
java -calsspath c:\classdir;.;c:\archives\archive.jar MyProg
整个指令必须写在一行中。将这样一个很长的命令行放在一个 shell 脚本或一个批处理文件中是个不错的主意。
利用 -classpath 选项设置类路径是首选的方法,也可以通过设置 CLASSPATH 环境变量来指定。具体细节依赖于所使用的 shell。在 Bourne Again shell(bash)中,命令如下:
export CLASSPATH=/home/user/classdir:.:/home/user/archives/archive.jar
在 Windows shell 中,命令如下:
set CLASSPATH=c:\classdir;.;c:\archives\archive.jar
直到退出 shell 为止,类路径设置均有效。
警告: 有人建议将 CLASSPATH 环境变量设置为永久不变的值,一般来说这是一个糟糕的想法。人们有可能会忘记全局设置, 因此,当他们的类没有正确地加载时,就会感到很奇怪。一个应该受到谴责的示例是 Windows 中 Apple 的 QuickTime 安装程序。很多年来,它都将 CLASSPATH 全局设置为指向它需要的一个 JAR 文件,而没有在类路径中包含当前路径。因此,当程序编译后却不能运行时,无数 Java 程序员不得不花费很多精力去解决这个问题。
警告: 过去,有人建议完全绕开类路径,将所有的文件都放在 jre/lib/ext
目录中。这种机制在 Java 9 中已经过时,不过不管怎样这都是一个不好的建议。很可能会从扩展目录加载一些已经遗忘很久的类,这会让人非常困惑。
在 Java 9 中,还可以从模块路径加载类。