打点Java类路径(Windows)
副标题#e#
类路径可以毗连 Java 运行库和文件系统。它界说编译器息争释器应该在那里查找要加载的 .class 文件。它的根基思想是:文件系统的条理布局反应了 Java 包的条理布局,而类路径则界说了文件系统中的哪个目次可以作为 Java 包条理布局的根。
遗憾的是,凡是文件系统很是巨大并依赖于平台,并且和 Java 包也不能很好地匹配。尤其是在 Windows 情况中更是如此。Java 是一些 Unix 好手设计的,因而从许多方面来说,这也就意味着它无法很好地与 Windows 约定同步。这样一来,岂论是新用户照旧资深 Java 措施员都深感类路径的棘手。没错,它简直不是 Java 平台好的一面,它让您到了下班的时候还在忙于调试一个顽固的小问题。
虽然回收 Eclipse 这样的优秀 IDE 可以淘汰打点类路径的一些坚苦,但只能说是一些,并且前提还必需是一切都正常(但这不大大概,因为总会有一些意外呈现)。因此,每个 Java 措施员都必需要全面相识类路径,惟有如此,才有但愿调试类路径中所呈现的问题。
在本文中,我给出了您所需要相识的有关 Windows 中的 Java 类路径(以及相关源路径)的全部内容。本文的 姊妹篇 则展示了 UNIX 和 Mac OS X 上的雷同技能。遵循这里列出的步调可以防备不须要的类路径妨碍并能办理呈现的大大都问题。
包布局
要把握类路径,首先应从其源代码入手。每个类都属于一个包,而此包必需 遵守尺度的定名约定。简朴地说,包的名称要由颠倒的两级域名开始,好比 com.example 或 edu.poly,之后是至少一个或多个单词用于描写包的内容。例如说,假设有一个域名为 elharo.com,假如要建设一个 Fraction 类,可以将其放入如下包中:
com.elharo.math
com.elharo.numbers
com.elharo.math.algebra.fields
在颠倒的域名之后,需要利用单一单词的子包名。不要利用缩写形式,并要担保拼写正确。假如需要,可以利用拼写查抄器。大部门与类路径相关的问题都是由在源代码中利用某个单词而在文件系统中利用的却是与之稍有差异的拼写或缩写而引起的。所以最好的做法就是老是利用拼写正确且没有缩写的名称。
整个包名称应该是小写的,纵然该名称是在别处常采纳大写形式的一些习用名称和缩写词。Windows 凡是不区分文件名中的巨细写,但 Java 和一些 UNIX 文件系统却区分。假如需要在差异的系统间移动文件,巨细写问题必定会带来一些贫苦。
包名称必需要全部由 ASCII 字符构成。一些编译器也接管用 Hebrew、Cyrillic、Greek 或其他剧本编写的包名称,但大大都文件系统并不接管;您稍后就会看到,这样的包名称必需担负充当目次名这样的双重任务。Java 包和类名是 Unicode,但许多文件系统(包罗 FAT)却不能识别 Unicode。遗憾的是,FAT 系统很是之多。假如只简朴地用差异的默认编码将文件复制到系统将会使编译器息争释器无法找到正确的类。
不要试图在包名称方面节省本钱。久远来看,这只会有百害而无一利。假如需要域名就买一个。假如名称太长就买个短些的(我曾经买到了 xom.nu 这样一个域名,因而我的包前缀就只有 6 个字符)。不要将类放到默认包中(默认包是指假如未在类中包括一条包语句时系统默认给出的包)。假如包会见倒霉于工具间的通信,就需要向类中添加更多的民众要领。需要多次利用的类必需要放到包中。
设置 Windows
文件扩展名和路径对付 Java 和 Windows 来说都很重要。所以在开始下面的步调之前,务必确保可以或许看到它们。埋没部门文件名对终端用户来说还可以接管(但我并不完全这么认为),但对付开拓人员来说显然不可。要办理这个问题,需要对 Windows Explorer 的一些默认配置做些变动。
首先在 Windows 桌面打开任意一个文件夹。找到 Tools 菜单,选择 Folder Options。在随后打开的对话框中,确认选择了如下三个选项,如图 1 所示:
“Display the full path in the address bar” 应该选中。
“Display the full path in title bar” 应该选中。
“Hide file extensions for known file types” 应该排除。
图 1. Windows Explorer 选项
您大概还想选中 “Show hidden files and folders”,它对您的 Java 功课没有多大影响,但就我小我私家而言,我很但愿可以或许看到所操纵的所有内容。选中这些选项可以显露关于所干工作的更多细节,使您能更轻松地调试所呈现的问题。
#p#副标题#e#
目次布局
下一步要做的是组织源文件来匹配包布局。在某处建设一个清洁的空缺目次。本文中,我将其定名为 project。假如将该目次放到根级别(好比 C:\project)就会更容易一些。也可以将其放到桌面或 Documents and Settings 文件夹。但这会让输入的呼吁更长也更为巨大,所以只在十分须要的环境下才这么做(假如您运行的是 Windows XP 或更高,并且没有打点员权限,那您没有什么其他的选择。在单用户系统上,最好是要有打点员权限,这会使环境大大简化)。
#p#分页标题#e#
无论如何,不要将此目次(或任何其他目次)放到 JDK 文件夹(譬喻C:\jdk1.6.0 或 C:\Program Files\Java\j2sdk1.5.0_09)。JDK 和 JRE 目次在初始安装之后应该保持不动。
在 project 目次内,别的建设两个目次: bin 和 src(一些人更喜欢将其别离定名为 build 和 source)。
接下来,在 src 目次,建一个与包条理布局相匹配的条理布局。譬喻,假如给定类名为 com.elharo.math.Fraction,我会将 com 目次放到 src 目次中,然后在 com 目次中建设一个 elharo 目次,再在 elharo 目次内放一个 math 目次,最后在 math 目次内放上 Fraction.java,如图 2 所示:
图 2. 切合包布局的目次布局
要点:不要在 src 目次中安解除源代码之外的任何内容。凡是这里放入的文件都是 .java 文件。在有些环境下,也可安排 .html 文件(用于 JavaDoc)或其他范例的源代码。然而,决不能在此布局内安排 .class 文件或任何其他编译并生成的工件。这样做只会带来贫苦。遗憾的是,假如不足审慎,javac 编译器就会 “州官放火”。在下一节,将先容如何修复这一问题。
后斜线和前斜线
开拓出 Java 的 UNIX 措施员答允编译器接管 UNIX 名目标前斜线用来取代 Windows 中的后斜线。因此,以下呼吁仍有效:
C:\project> javac src/com/elharo/math/Fraction.java
然而,一些呼吁不答允利用前斜线,要求必需利用后斜线。在 Windows 中利用后斜线最为容易。
编译
编译 Java 代码需要一些能力,原因是必需要跟踪如下几方面相关但又有所差异的内容:
正在编译的方针文件。
编译器在个中寻找方针文件导入 .java 文件的谁人目次。
编译器在个中寻找方针文件导入 .class 文件的谁人目次。
编译器在个中安排编译输出的目次。
默认地,javac 编译器将上述目次都认为是当前目次,而这并不是您所但愿的。因此,需要在编译时显式地指定这些元素。
要编译的文件
指定的第一个要编译的文件是 .java 文件,以从当前目次到该文件的整个路径的形式给出。好比,假设当前地址目次是 图 1 所示的 project 目次。该目次包括 src 目次。此 src 目次包括 com 目次,而 com 目次又包括 example 目次,example 目次下是 Fraction.java 文件。如下呼吁行对它举办编译:
C:\project> javac src\com\elharo\math\Fraction.java
假如路径不正确,就会给出这样的错误动静:
error: cannot read: src\com\example\mtah\Fraction.java
假如呈现这样的错误动静,就需要查抄路径的各个部门,确保它们拼写正确。在本例中,错误动静显示 math 的第二和第三个字母颠倒了。
假如没有发明拼写错误,请查抄该文件是否处于它应该呈现的位置,要领是按如下所示发出一个 dir 呼吁:
C:\project\src> dir src\com\example\math
ls: src/com/example/math: No such file or directory
呈现问题的原因凡是是因为路径拼写错误,但也大概是由于当前的目次差池。在本例中,需要查抄当前的事情目次是不是 project 目次。查抄呼吁行上 C:和 >之间的文本,以确认当前目次是否是预期的目次。在本例中,当前目次为 C:\project\src,而预期的目次却是 C:\project。
输出到那里?
假设没有呈现任何语法错误,javac 将编译后的 .class 文件放到与之对应的.java 文件地址的沟通目次内。这并不是您所想要的功效。将 .class 和 .java 文件混在一起经常会使清理编译后的文件十分坚苦,因为很大概会心外删除本应保存的 .java 文件。这常会使清理构建十分坚苦,并且还会导致版本问题。宣布一个二进制时,只对编译后的 .class 文件举办归档也会十分坚苦。因此,需要奉告编译器将编译后的输出放到一个完全差异的目次内。-d 开关用来指定输出目次(凡是称为 bin、build 或 class):
C:\project> javac -d bin src\com\elharo\math\Fraction.java
这时的输出将如图 3 所示。请留意 javac 已经建设了完整的com\elharo\math目次条理布局。不需要手动成立。
图 3. 并行源和编译后的条理布局
源路径
#p#分页标题#e#
源路径 就是 Java 在个中寻找源文件的谁人目次。详细到本例,就是 src 目次。该目次必需包括源文件的条理布局,这些源文件可以被放到它们本身的目次中。因此它不是 com 目次也不是src\com\elharo\math目次。
许多项目都利用不止一个类和包。它们通过导入语句和完整的包限定类名毗连起来。譬喻,假设您在 com.elharo.gui 包中建设了 MainFrame 类,如清单 1 所示:
清单 1. 一个包中的类可以导入另一个包中的类
package com.elharo.gui;
import com.elharo.math.*;
public class MainFrame {
public static void main(String[] args) {
Fraction f = new Fraction();
// ...
}
}
该类利用的是与 MainFrame 类地址的包差异的包中的 com.elharo.math.Fraction 类。源配置此刻应该如图 4 所示(我将编译后的输出从之前的步调中删除了。但这没有干系,因为我老是能从头编译它)。
图 4. 几个包的源布局
此刻来看一下试着像以前一样编译 MainFrame.java 会呈现什么环境。
清单 2. 编译 MainFrame.java
C:\project> javac -d bin src\com\elharo\gui\MainFrame.java src\com\elharo\gui\MainFrame.java:3: package com.elharo.math does not exist import com.elharo.math.*; ^ src\com\elharo\gui\MainFrame.java:7: cannot find symbol symbol : class Fraction location: class com.elharo.gui.MainFrame private Fraction f = new Fraction(); ^ src\com\elharo\gui\MainFrame.java:7: cannot find symbol symbol : class Fraction location: class com.elharo.gui.MainFrame private Fraction f = new Fraction(); ^ 3 errors
呈现清单 2 中的错误的原因是,固然 javac 知道到那里可以找到 MainFrame.java,但它却并不知道到那里可以找到 Fraction.java(您大概以为它应该具备足够的智能来识别匹配的条理布局,但事实并非如此)。为了给它提供一些线索,必需指定源路径。用源路径指定编译器应该到哪些目次查找源文件的条理布局。在清单 2 中,源路径是 src。所以我利用了 -sourcepath 选项,如下所示:
C:\project> javac -d bin -sourcepath src src\com\elharo\gui\MainFrame.java
此刻再编译措施,就不会呈现错误,并会发生如图 5 所示的输出。请留意 javac 也编译了文件 Fraction.java,Fraction.java 被当前编译的文件引用。
图 5. 多类输出
编译源路径中的多个目次
在源路径中可以有多个目次,利用分号脱离各目次,但凡是没有须要这么做。譬喻,若我想包罗当地的 src 目次和用来存放另一个项目标源代码的C:\Projects\XOM\src目次,我可以这样举办编译:
C:\project> javac -d bin -sourcepath src;C:\Projects\XOM\src
src/com/elharo/gui/MainFrame.java
该呼吁并不编译在这两个条理布局中所找到的每个文件。它只编译由单个的 .java 文件直接或间接引用的文件,而此 .java 文件必需被编译。
更常见的环境是,为 .java 文件用一个单一的源目次,为类或安排了预编译的第三方库的 JAR 归档文件用多个目次。而这正是类路径的浸染地址。
目次名中的空格
Java 类名和包名都不能包括空格。但有时一个包括 Java 包目次或源文件的目次可以包括空格。Documents and Settings 是个中一个最为明明的例子。假如需要在路经中包罗一个这样的目次,就必需在相关的呼吁行参数两头加上双引号。譬喻,当前目次为根目次 C:,src 文件夹在 C:\Documents and Settings\Administrator\project,则需要像下面这样对它举办编译:
C:\> javac -d bin -sourcepath "C:\Documents and Settings\Administrator\project"
-classpath C:\lib\classes
"C:\Documents and Settings\Administrator\project\src\com\elharo\gui\MainFrame.java"
在许多环境下,大可不必这样做,您完全可以在编译和运行措施前先转换到 project 目次。
配置类路径
在大中型项目中,每次都要对每个文件举办重编译会很是耗时。为淘汰这种编译承担,可以在差异的 bin 目次别离编译和存储沟通项目标独立部门。这些目次被添加到类路径。
将类添加到类路径有几种要领可选。但您只能利用 -classpath呼吁行开关。譬喻,假设我想从另一个之前已经编译到目次 C:\lib\classes 中的项目导入文件,那么我会向呼吁行添加 -classpath C:\lib\classes,如下所示:
C:\project> javac -d bin -sourcepath src -classpath C:\lib\classes
src\com\elharo\gui\MainFrame.java
此刻假设需要添加两个目次:C:\project1\classes 和 C:\project2\classes,可以将它们用分号分隔,如下所示:
C:\project> javac -d bin -sourcepath src
-classpath C:\project1\classes;C:\project2\classes
src\com\elharo\gui\MainFrame.java
#p#分页标题#e#
虽然,您也可以利用本身喜欢的各类相对路径的名目。好比,假如 project1 和 project2 是当前事情目次的同级目次(即它们有沟通的父目次),那么我会这样引用它们:
C:\project> javac -d bin -sourcepath src
-classpath ..\project1\classes;..\project2\classes
src\com\elharo\gui\MainFrame.java
到今朝为止,我都一直假设措施完全独立而且没有利用任何单独的编译后的第三方库。假如需要利用第三方库,还必需将它们也添加到类路径。库凡是是 JAR 文件的名目,好比 junit.jar 或 icu4j.jar。在本例中,需要向类路径添加的只是 JAR 文件自己,而不是包括 JAR 文件的目次(从实质上讲,JAR 文件可以充当包括编译后的 .class 文件的一种目次)。譬喻,如下呼吁会向类路径添加三项内容:目次 C:\classes、当前事情目次中的文件 icu4j.jar 以及 E:\lib 中的文件 junit.jar:
C:\project> javac -d bin -sourcepath src
-classpath C:\classes;icu4j.jar;E:\lib\junit.jar
src\com\elharo\gui\MainFrame.java
JAR 文件仅用于 .class 文件和类路径,不消于 .java 文件和源路径。
顶级目次
请留意这里所说的顶级目次是指所有包括包的条理布局的顶级目次。具有匹配包名称(com、elharo、math 等)的目次毫不会直接包罗在源路径或类路径中。
运行措施
此刻您已经乐成地编译了措施,可以运行它了。运行与编译相似但更为简朴一些。当运行措施时,只需指定两项内容:
类路径
包括 main() 要领的类的完全限定包名
无需指定源路径。
凡是这里的类路径与编译措施所利用的类路径沟通,只是多了一个安排编译后的输出的目次。譬喻,假如编译呼吁如下所示:
C:\project> javac -d bin -sourcepath src
-classpath C:\classes;E:\lib\junit.jar
src\com\elharo\gui\MainFrame.java
而且 main() 要领在类 com.elharo.gui.Mainframe.java 内,就可以像这样运行此措施:
C:\project> java -classpath C:\classes;E:\lib\junit.jar
com.elharo.gui.MainFrame
请务必留意呼吁行的最后一项是类名。它不是一个文件名,也不是 .java 或 .class。该类必需可以或许在类路径的某处找到。
大概存在类的其他处所
我强烈发起您在编译和运行时老是显式地指定类路径。也可以将文件放到其他处所,以便它们可以被添加到类路径中,并被 javac 编译器和 java 表明器找到。这种做法会节减一些键入操纵,但当(留意不是假如)您无意间将一个旧版本的类放到类路径中时,这却会淹灭大量的调试时间。
在本节,将展示类经常隐匿个中的几个所在,这些类很大概会出乎料想地冒到类路径中并导致问题的呈现。在不受您节制的呆板上(好比处事器),这更为多见。
当前事情目次
编译器老是将当前事情目次 (.) 添加到类路径,而不管您是否曾显式地要求这样做。您很容易健忘在和您地址的目次沟通的目次中有和没有的内容。因此,请只管制止将任何类或条理布局放入 project 或 home 目次。相反地,应该将 .java 文件和 .class 文件别离放入 src 目次和 bin 目次。
CLASSPATH
过一会,您就会发明向类路径手工添加 bin 目次和 JAR 归档文件过分繁琐。这时您大概会想要利用 CLASSPATH 情况变量。可以只向 CLASSPATH 情况变量添加一次目次和 JAR 归档文件,之后就不需要在每次运行 javac 或 java 时都要再键入这些路径。
请务必抵抗这种诱惑。这样做,一旦加载了错误的类或错误版本的类,就会出问题。并且意外加载错误的类所带来的调试时间经常会百倍于省下的那点键入时间。要制止输入并自动处理惩罚类路径有更好的要领。
jre\lib\ext
放在jre\lib\ext目次中的 JAR 归档文件会被添加到通过虚拟机运行的所有应用措施的类路径。这看起来很利便,实际上它与向 CLASSPATH 情况变量添加目次一样,存在久远的潜在问题。您早晚(凡是很快)会在您想都想不到的处所加载类的一个错误版本的类并会为此支付大量的调试时间。
陈设一个处事器端的应用措施时,问题就更为严峻。请确保陈设到的处事器在其jre\lib\ext目次没有任何特另外 JAR。假如您不熟悉错误症状,也不知道该如何查找,那么由类路径中的错误版本的 JAV 归档文件所带来的问题大概会很是难于调试。为了制止这些问题的呈现,一些框架甚至编写了本身的类加载器,用来绕过 Java 代码凡是的类加载机制。
jre\lib\endorsed
#p#分页标题#e#
放在jre\lib\endorsed目次中的 JAR 文件也被添加到了通过虚拟机运行的所有应用措施的类路径。差异的是,这里的文件被实际放入了 bootclasspath 而不是凡是的类路径,并可以取代 JDK 附带的尺度类。这种方法对付在 VM 更新 XML 理会器和修复 bug 尤其有用。
可是,如前所述,这种要领看起来十分利便,但实际上也存在恒久的潜在问题,原因也一样。假如需要替换 JDK 类,可以在运行时利用 -Xbootclasspath/p 选项来制止意外地加载错误版本的类。
C:\project> java -classpath C:\classes
-Xbootclasspath/p:xercesImpl.jar com.elharo.gui.MainFrame
自动打点类路径
在想要利用电动射钉枪之前要先纯熟利用锤子,与此相似,在试图回收更强大的自动打点东西之前也要先能自如地手动打点这些类。假如您把握了呼吁行东西集,就可以利用别的的东西来自动处理惩罚源路径和类路径所需的一些繁琐进程。这些东西大部门也需要您像本文所先容的那样组织文件。
IDE
像 Eclipse 和 NetBeansMost 这样的很多开拓情况都能协助类路径的自动打点。譬喻,当变动包的名称时,Eclipse 能相应地移动对应的 .java 文件,如图 6 所示:
图 6. 在 Eclipse 中快速修复类路径
请记着,这些 IDE 位于文件系统的顶部,必需正确配置,尤其是当需要与其他东西和其他 IDE 集成时就更应如此。这些东西最大的孝敬是用 GUI 对话框、树视图和选项卡取代了呼吁行开关参数,但其根基的文件布局照旧一样的。
Ant
Ant 是自动化构建进程的事实上的尺度东西。与将目次放在jre\lib\ext或 CLASSPATH 情况变量的做法差异,Ant 真的可以让您建设单步的构建进程。但您仍然需要在 Ant build.xml 配置类路径并手动将源文件放到正确的目次。但至少此刻您无需在每次编译都要从头举办指定。
Maven
Maven 在组织和自动化构建进程方面比 Ant 还要更进一步。Maven 提供一个公道的默认配置让您可以通过添加少许几行代码并将源文件放到 Maven 可以或许找到的位置即可构建简朴的项目。您仍然需要调解文件系统和包的条理布局。Maven 在打点第三方库的依赖性方面也有上佳的表示,固然它不如 Ant 那么易于定制。
竣事语
不管类路径有何等棘手,您都可以通过一些简朴的法则对它加以管束,尤其是要记着如下的一些原则:
将类放到包中。
严格遵守包和类的定名约定和巨细写约定。
确保包的条理布局与目次的条理布局匹配。
老是对 javac 应用 -d 选项。
不要在jre\lib\ext内放任何对象。
不要在jre\lib\endorsed内放任何对象。
不要将 .java 文件与 .class 文件放在同一个目次。
不要将任何 .java 或 .class 文件放在当前的事情目次。
最后一点提示:许多耗时的类路径问题的起因多半是目次名拼写错误或从错误目次举办了编译。假如您不能找到问题的地址,可以问问周围的伴侣或同事。以我的履历,本身发明本身的错误老是坚苦的,但这些错误在别人看来却显而易见。所以寻求他人的辅佐也是一种切实有效的调试能力。
类路径确实不是个简朴的问题,但总会有相应的应对要领,所以它是完全可打点的。些许的审慎加上对本文所先容的定名约定、呼吁行参数和目次布局的留意,应该可以或许使您在问题最少的环境下编译和运行措施了。