类路径是Java运行时与文件系统之间的连接。 它定义了解释器在哪里寻找要加载的.class文件。 基本思想是文件系统层次结构反映Java包层次结构,而类路径指定文件系统中的哪些目录充当Java包层次结构的根。

不幸的是,文件系统非常复杂,并且非常依赖平台,并且它们与Java软件包并不完全匹配。 因此,多年来,类路径一直困扰着新用户和经验丰富的Java程序员。 它不是Java平台的漂亮部分。 正是这种烦人的故障使您无法在下午5点之前正常工作,试图调试一个顽固地拒绝解决方案的小问题。

像Eclipse这样的优秀IDE可以使您摆脱管理类路径的某些困难,但这仅在一定程度上且仅在没有问题的情况下(并且总有问题)。 因此,至关重要的是,每个Java程序员都必须完全理解类路径。 只有深入了解,您才能调试由类路径引起的棘手问题。

在本文中,我列出了您需要了解的有关UNIX,Linux和Mac OS X上的Java类路径(以及关联的源路径)的所有信息。在随附的文章中 ,我演示了Windows的类似技术。 遵循此处概述的过程将作为指导,并应解决大多数类路径问题。

包装结构

掌握类路径始于源代码。 每个类都属于一个包,并且该包必须遵循标准的命名约定。 简要回顾一下:程序包名称以两级反向域名开头,例如com.exampleedu.poly 。 紧随其后的是至少一个用于描述软件包内容的单词。 例如,因为我拥有域名elharo.com,所以如果要编写Fraction类,可以将其放在以下软件包之一中:

  • com.elharo.math
  • com.elharo.numbers
  • com.elharo.math.algebra.fields

在反向域名之后,仅使用单字子包名称。 不要缩写,并正确拼写所有单词。 如果需要,请使用拼写检查器。 与类路径相关的大部分问题是由于在源代码中使用一个单词,而在文件系统中对该单词的拼写或缩写稍有不同。 唯一明智的选择是始终使用正确拼写的未缩写名称。

整个软件包的名称都应小写,即使对于通常使用大写字母的专有名称和首字母缩写词也是如此。 软件包名称应仅由ASCII字符组成。 尽管编译器接受以希伯来语,西里尔字母,希腊语和其他脚本编写的程序包名称,但许多文件系统却不接受。 不久您将看到,这些软件包名称将必须担当目录名称的双重职责。 因此,包(和类)名称应限制为ASCII。 (虽然Java包和类名是Unicode,但许多文件系统尚不支持Unicode。只需将文件复制到具有不同默认编码的系统,就可以防止编译器和解释器找到正确的类。)

丢码

如果您只是编写一个类来测试您对API的直觉,并且在运行一次之后将其立即丢弃,那么您不必将其放在包中。 但是,任何将多次使用的类都应放在一个包中。

不要忽略您的包裹名称! 从长远来看,这只会导致灾难。 如果您需要域名,请购买一个。 如果名称太长,请购买较短的名称。 (我曾经购买过xom.nu,所以我的包前缀只有六个字符。)不要将类放在默认包中(如果您在类中不包含package语句,则得到的包)。 如果程序包访问阻止对象进行通信,请向类添加更多公共方法。 您多次使用的每个类都必须放在一个包中。

目录结构

下一步是组织源文件以匹配包结构。 在某处创建一个干净的空目录。 出于本文的目的,我将其命名为project 。 在此目录中,再创建两个目录:bin和src。 (有些人喜欢分别命名这些内部版本和源。)

接下来,在src目录中,建立一个与包层次结构相对应的层次结构。 例如,给定一个名为com.elharo.math.Fraction的类,我将在src目录中放置一个com目录。 然后,我将在com目录中创建一个elharo目录。 然后,我将数学目录放在elharo目录中。 最后,我将Fraction.java放入此数学目录中,如图1所示:

图1.目录结构遵循包结构

您可以使用一个mkdir -p命令执行此操作:

$ mkdir -p com/elharo/math

非常重要:切勿在源代码目录中放置源代码以外的任何内容。 通常,您放入的唯一文件是.java文件。 有时,您可以在该目录中放置.html文件(对于Javadoc)或其他类型的源代码。 但是,您永远不想将.class文件或其他编译的,生成的工件放入此层次结构中。 这样做是灾难的根源。 可悲的是,除非您小心,否则javac编译器将完全做到这一点。 在下一节中,我将向您展示如何解决该问题。

编译中

编译Java代码非常棘手,因为您需要跟踪一些相关但不同的事情:

  • 您正在编译的目标文件。
  • 编译器在其中查找目标文件导入的.java文件的目录。
  • 编译器在其中查找目标文件导入的.class文件的目录。
  • 编译器放置已编译输出的目录。

默认情况下,javac编译器认为所有这些都是当前的工作目录,几乎从来都不是您想要的目录。 因此,在编译时,您需要显式指定每个元素。

编译文件

您指定的第一件事是要编译的.java文件。 这是从当前工作目录到该文件的路径。 例如,假设您位于图1所示的项目目录中。 该目录包含一个src目录。 src目录包含com目录,其中包含示例目录,其中包含Fraction.java文件。 以下命令行对其进行编译:

$ javac src/com/elharo/math/Fraction.java

如果路径不正确,您将收到如下错误消息:

error: cannot read: src/com/example/mtah/Fraction.java

如果您看到此错误消息,请检查路径的每一部分以确保其拼写正确。 然后通过执行如下所示的ls来检查文件是否确实在预期的位置:

$ ls src/com/example/math
ls: src/com/example/math: No such file or directory

此问题通常表明路径输入错误,但也可能意味着您不在自己认为的目录中。在此示例中,您将检查当前工作目录是否为项目目录。 pwd命令在这里很有用。 例如,以下内容告诉我实际上我在project / src中,而不是在项目目录中:

$ pwd
/Users/elharo/documents/articles/classpath/project/src

我需要cd ..才能编译。

输出到哪里

假定没有语法错误,javac会将已编译的.class文件放在.java文件所在的目录中。 您不想要这个。 混合使用.class和.java文件,很难清除已编译的文件,而又不小心删除要保留的.java文件。 这使干净的版本成为问题,并倾向于导致版本问题。 分发二进制文件时,也很难仅打包已编译的.class文件。 因此,您需要告诉编译器将编译后的输出放在一个完全不同的目录中。 -d开关指定输出目录(通常称为bin,build或classes):

$ javac -d bin src/com/elharo/math/Fraction.java

现在的输出如图2所示。请注意,javac已经创建了完整的目录com / elharo / math层次结构。 您不需要手动进行。

图2.并行源和编译层次结构

源路径

Java查找源文件的目录称为sourcepath 。 在此处概述的方案中,这是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类。 现在,源设置如图3所示。(我已从上一步中删除了编译后的输出。我随时可以再次对其进行编译。)

图3.几个软件包的源结构

现在让我们看看像以前一样尝试编译MainFrame.java时会发生什么:

清单2.编译MainFrame.java
$ 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.MainFrameprivate Fraction f = new Fraction();^
src/com/elharo/gui/MainFrame.java:7: cannot find symbol
symbol  : class Fraction
location: class com.elharo.gui.MainFrameprivate Fraction f = new Fraction();^
3 errors

之所以发生清单2中的错误,是因为尽管javac知道在哪里可以找到MainFrame.java,但是它不知道在哪里可以找到Fraction.java。 (您可能会注意到匹配的程序包层次结构足够聪明,但是事实并非如此。)为了说明这一点,我必须指定sourcepath 。 这指定了编译器在其中查找源文件层次结构的目录。 在清单2中,它是src。 因此,我使用-sourcepath选项,如下所示:

$ javac -d bin -sourcepath src src/com/elharo/gui/MainFrame.java

现在,程序进行编译,没有错误,并产生图4所示的输出。请注意,javac还编译了Fraction.java文件,该文件由我正在编译的文件引用。

图4.多类输出

在源路径中编译多个目录

实际上,您的源路径中可以有多个目录,以冒号分隔,尽管通常不需要这样做。 例如,如果我要同时包含本地src目录和目录/ Users / elharo / Projects / XOM / src,而我保留了另一个项目的源代码,则可以这样编译:

$ javac -d bin -sourcepath src:/Users/elharo/Projects/XOM/srcsrc/com/elharo/gui/MainFrame.java

此命令不会编译在这两个层次结构中找到的每个文件。 它仅编译由我明确要求进行编译的单个.java文件直接或间接引用的文件。

更常见的是,您将为.java文件提供一个源目录,但为类或JAR归档文件提供了多个目录,并在其中放置了预编译的第三方库。 这是类路径的角色。

设置类路径

在大中型项目中,每次重新编译每个文件都会很耗时。 您可以通过在不同的类或bin目录中分别编译和存储同一项目的独立部分来减轻此负担。 这些目录被添加到类路径中。

有几种将类添加到类路径的方法。 但是, -classpath命令行开关是唯一应使用的开关。 例如,假设我要从以前编译到目录/ Users / elharo / classes的另一个项目中导入文件。 然后,我将-classpath /Users/elharo/classes到命令行,如下所示:

$ javac -d bin -sourcepath src -classpath /Users/elharo/classessrc/com/elharo/gui/MainFrame.java

现在,假设我需要添加两个目录,/ Users / elharo / project1 / classes和/ Users / elharo / project2 / classes。 然后,我将它们都包括在内,并用冒号隔开,如下所示:

$ javac -d bin -sourcepath src-classpath /Users/elharo/project1/classes:/Users/elharo/project2/classessrc/com/elharo/gui/MainFrame.java
顶层目录

请注意,我在这里引用的目录都是所有顶级目录,其中包含com / elharo / foo / bar或nu / xom / util之类的层次结构。 名称与包名称(com,elharo,math等)匹配的目录永远不会直接包含在源路径或类路径中。

当然,如果愿意,可以使用各种形式的相对路径。 例如,如果project1和project2是当前工作目录的兄弟(即它们具有相同的父目录),那么我可以这样引用它们:

$ javac -d bin -sourcepath src-classpath ../project1/classes:../project2/classessrc/com/elharo/gui/MainFrame.java

到目前为止,我已经假定该程序是完整的,并且不使用任何单独编译的第三方库。 如果是这样,您还需要将它们添加到类路径中。 库通常以JAR文件的形式分发,例如junit.jar或icu4j.jar。 在这种情况下,添加到类路径的是JAR文件本身,而不是包含该文件的目录。 (本质上,JAR文件充当包含编译的.class文件的目录。)例如,以下命令将三件事添加到类路径:目录/ Users / elharo / classes,当前工作目录中的文件icu4j.jar。目录,以及/ Users / elharo / lib中的文件junit.jar:

$ javac -d bin -sourcepath src-classpath /Users/elharo/classes:icu4j.jar:/Users/elharo/lib/junit.jarsrc/com/elharo/gui/MainFrame.java

JAR文件仅用于.class文件和类路径,而不用于.java文件和源路径。

运行程序

现在,您已经成功编译了程序,并准备运行它。 这类似于但比编译简单。 运行程序时,只需指定两件事:

  • 类路径。
  • 包含main()方法的类的完全包装合格名称。

您无需指定源路径。

通常,类路径与用于编译程序的类路径相同,并添加了放置编译输出的目录。 例如,如果compile命令是这样的:

$ javac -d bin -sourcepath src-classpath /Users/elharo/classes:/Users/elharo/lib/junit.jarsrc/com/elharo/gui/MainFrame.java

并且main()方法位于com.elharo.gui.MainFrame类中,那么您将像这样运行程序:

$ java-classpath bin:/Users/elharo/classes:/Users/elharo/lib/junit.jarcom.elharo.gui.MainFrame

请注意,命令行上的最后一项是类名 。 它不是文件名。 它不以.java或.class结尾。 此类必须在类路径的某个位置找到。

其他地方的课程

我强烈建议您在编译和运行时始终明确指定类路径。 您可以在其他地方放置文件,以便将它们添加到类路径中,并由javac编译器和java解释器找到。 这些选项只保存少量的键入,而这样做的代价是要进行大量的调试(如果不是,则是不小心将旧版本的类放在类路径中)。

在本节中,我将向您展示一些您可能希望找到隐藏类的地方,这些地方意外地弹出您的类路径并引起问题。 这尤其可能发生在您无法控制的机器(例如服务器)上。

当前工作目录

编译器使用当前工作目录(。)作为默认类路径。 但是,一旦以其他方式设置了类路径(例如,使用-classpathCLASSPATH环境变量),该路径就不再是自动的。 您必须像其他目录一样将当前工作目录添加到类路径中。 无论哪种情况,都很容易忘记与您所在目录相同或不同的目录。 因此,请尝试避免将任何类或层次结构放入您的项目或主目录中。 相反,请始终将内容整齐地分成.java文件的src目录和.class文件的bin目录。

类路径

一段时间后,您可能会厌倦了手动将bin目录和JAR存档添加到类路径。 然后,您可能会发现CLASSPATH环境变量。 您只能将目录和JAR归档一次添加到CLASSPATH环境变量中。 这样,您不必在每次运行javac或java时都键入它们的路径。

抵制这种诱惑。 当您加载错误的类或错误的类版本时,它将导致问题。 您现在保存的任何时间,都会因调试错误而意外地加载错误的类,从而使您退回一百次。 有更好的方法可以自动执行类路径并避免键入。

jre / lib / ext

放置在jre / lib / ext目录中的JAR归档文件将添加到使用该虚拟机运行的所有应用程序的类路径中。 尽管这看起来很方便,但是这也是一个长期的错误,类似于将目录添加到CLASSPATH环境变量中。 迟早(可能更快),您将从甚至根本没有考虑过的地方加载错误版本的类,并浪费大量时间进行调试。

部署服务器端应用程序时,此问题尤其严重。 请注意,要部署到的服务器的jre / lib / ext目录中没有任何额外的JAR。 如果您不了解症状或不知道要查找的内容,则很难调试由类路径中的JAR存档版本错误导致的问题。 为了避免这些问题,有些框架甚至编写了自己的类加载器,以绕过Java代码的常规类加载机制。

jre / lib /认可

jre / lib / endorsed目录中的JAR文件也将添加到使用该虚拟机运行的所有应用程序的类路径中。 区别在于,这些文件实际上是添加到bootclasspath中,而不是通常的classpath中,并且可以替换JDK附带的标准类。 该方法对于升级XML解析器和修复VM中的错误特别有用。

再一次,尽管这种技术看起来很方便,但出于相同的原因,这也是一个长期的错误。 如果需要替换JDK类,请在运行时使用-Xbootclasspath/p选项,以避免意外加载错误版本的类:

$ java -classpath /Users/elharo/classes-Xbootclasspath/p:xercesImpl.jar com.elharo.gui.MainFrame

自动化类路径管理

在拿起钉枪之前,您应该学会使用锤子。 同样,在尝试使用功能更强大的工具之前,您应该可以手动管理类。 但是,有些工具旨在减轻处理sourcepath和classpath的痛苦。 通常,它们是按照我在本文中介绍的方式为您组织文件来实现的。

集成开发环境

诸如Eclipse和NetBeans之类的大多数集成开发环境都可以自动化并在类路径管理的某些方面提供帮助。 例如,当您更改程序包名称时,Eclipse提供了移动相应的.java文件以使其匹配的功能,如图5所示:

图5. Eclipse中的类路径的快速修复

但是请记住,这些IDE仍位于必须正确设置的文件系统之上,尤其是在需要与其他工具和其他IDE集成的情况下。 这些工具的主要作用是GUI对话框,树形视图和选项卡替代了命令行开关。 但是基本文件结构是相同的。

蚂蚁

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文件放在当前工作目录中。

最后一个提示:类路径的许多耗时的问题都围绕简单的错误,例如拼写错误的目录名或从错误的目录进行编译。 如果您根本无法解决问题,请请朋友或同事查看您的问题。 通常,我发现我太接近问题了,无法在我的设置中看到对其他任何人都显而易见的错误。 第二双眼睛是一种非常有效的调试技术。

类路径当然不容易,但是有一种疯狂的方法并且可以管理。 稍微注意一下命名约定,命令行参数和目录结构,应该可以使您以最小的麻烦来编译和运行程序。


翻译自: https://www.ibm.com/developerworks/java/library/j-classpath-unix/index.html

java mac转unix_管理Java类路径(UNIX和Mac OS X)相关推荐

  1. java 源码哪个文件夹_JAVA项目——项目编译后的类路径和源码文件夹图解

    JAVA项目--项目编译后的类路径和源码文件夹图解 前言: 一定要很清楚哪个是源码文件夹,哪个是类路径以及类路径的形成机制. 项目编译后不同的源码文件夹会被合并到bin目录下,形成类路径.不同的源码文 ...

  2. 形式参数内存在哪java_深入浅出Java中JVM内存管理

    原标题:深入浅出Java中JVM内存管理 Java岗位面试,JVM是对程序员基本功考察,通常会问你对JVM了解吗?可以分几部分回答这个问题,首先JVM内存划分 | JVM垃圾回收的含义 | 有哪些GC ...

  3. java jvm内存模型_Java(JVM)内存模型– Java中的内存管理

    java jvm内存模型 Understanding JVM Memory Model, Java Memory Management are very important if you want t ...

  4. 理解类路径是什么意思?如何运用包?

    昨天的文章中简单的介绍了包,今天就包的应用,在介绍运用包的例子之前,关于类路径环境变量的简单讨论是必要的.当包从访问控制和名称-空间-冲突中解决很多问题时,在编译和运行程序时它们导致某些古怪的难点. ...

  5. 苹果电脑怎么打开计算机管理,mac开机启动管理怎么设置_mac如何设置开机启动管理-win7之家...

    在使用mac电脑的过程中,有些用户由于安装软件时忘记了设置程序启动,导致每次在打开mac电脑时总会伴随着一些软件的自动开启,严重影响到电脑的启动速度,这时我们就可以对mac开机启动管理进行重新设置,那 ...

  6. macos 致命错误: 在类路径或引导类路径中找不到程序包 java.lang

    本文环境:MacOS 程序运行没有问题,但在 maven 打包时,报了如下错误: 致命错误: 在类路径或引导类路径中找不到程序包 java.lang[INFO] ------------------- ...

  7. eclipse识别不出java项目_Eclipse项目无法识别Java项目; 类路径问题

    我是一个非常缺乏经验的程序员.我刚刚在Eclipse中学习了一周的Java编程课程.当我回到家并在家用计算机上安装Eclipse时,我能够调出我在编辑器中创建的程序,但Eclipse不会运行它;它只接 ...

  8. Java相对路径与类路径详解

    目录 相对路径 类路径 总结 如果涉及文件相关的编程,文件路径这一块肯定是避不开的. 以前一直对Java中的相对路径理解的不是很深刻,今天又重新理解了一下,整理如下. 绝对路径就不多说了,linux中 ...

  9. java实现进程管理,Java调用批处理或可执行文件和Runtime、Process类实现Java版进程管理器...

    Java调用批处理或可执行文件 用Java编写应用时,有时需要在程序中调用另一个现成的可执行程序或系统命令,这时可以通过组合使用Java提供的Runtime类和Process类的方法实现.下面是一种比 ...

  10. java rabbitmq 工具类_RabbitMq通用管理工具类

    import java.io.IOException; import java.util.concurrent.TimeoutException; import com.rabbitmq.client ...

最新文章

  1. Python:KNN
  2. python 遍历文件夹和文件
  3. keras/tensorflow 模型保存后重新加载准确率为0 model.save and load giving different result
  4. 何恺明团队新作!深度学习网络架构新视角:通过相关图表达理解神经网络
  5. 用PHP代码实现简单的工厂模式,用PHP代码实现简单的工厂模式
  6. 学python买什么书好-python官方推荐30本面向初学者的书籍!你看过几本?
  7. 【PC工具】200324更新百度网盘下载工具——最新百度网盘下载工具使用方法及注意事项...
  8. html 两个iframe重叠,解决同一页面中两个iframe互相调用jquery,js函数的方法
  9. 图像转换为二维数组存入DSP6748
  10. 数据结构火车订票系统C语言课程设计,求助一个数据结构C语言课程设计源代码订票系统^:^!...
  11. Filter和Listener-学习笔记02【Filter细节】
  12. window document
  13. 【学习笔记】结合代码理解设计模式 —— 代理模式(静态代理、动态代理、延伸)
  14. Gradient-Based Learning Applied to Document Recognition 部分阅读
  15. word回车后间距太大_关于Word自动编号你知道多少?
  16. 【iOS】UITabView/UICollectionView 全选问题
  17. 程序员书单 (不定期更新)
  18. 毕设题目:Matlab语音隐写
  19. e680 reboot的研究
  20. 知识小结------数据分析------Cox比例风险回归模型(proportional hazards model)

热门文章

  1. RV-LINK:输出非预期响应向 GDB 报告错误
  2. 读名老中医之路笔记(三)
  3. 产品新创意,创意产品原型大公开,原来可以这样做!
  4. 心血管疾病:评估驾驶适应性(英国DVLA)
  5. Python免费的验证码识别
  6. 天镜漏洞扫描报告HTML转Excel格式Python脚本
  7. 牛逼哄哄的数据库连接池,底层原理是个啥?
  8. mybatisPlus实现创建时间、更新时间自动添加
  9. Swift 优化OC接口 NS_REFINED_FOR_SWIFT
  10. 智能空调雷达感应,雷达模组技术方案,毫米波雷达传感器应用