新Java开发人员经历的一个常见问题是,他们的程序无法通过错误消息运行:Could not find or load main class ...

这是什么意思,是什么原因造成的?您应该如何修复它?

答案

java <class-name>命令语法

首先,您需要了解使用以下命令启动程序的正确方法java(或者javaw) 命令。

正常语法^1^这是:

    java [ <options> ] <class-name> [<arg> ...]

在哪里<option>是命令行选项(以" - “字符开始),<class-name>是一个完全合格的Java类名称,并且<arg>是一个任意的命令行参数,将传递给您的应用程序。


^1-在本答案末尾附近描述了其他一些语法。^

该类的完全合格的名称(FQN)通常像您在Java源代码中一样书写;例如

    packagename.packagename2.packagename3.ClassName

但是某些版本的java命令允许您使用斜线而不是周期;例如

    packagename/packagename2/packagename3/ClassName

(令人困惑)看起来像一个文件路径名,但不是一个。请注意该术语完全合格的名称是标准的Java术语…不是我只是为了使您感到困惑:-)

这是一个例子java命令应该看起来像:

    java -Xmx100m com.acme.example.ListUsers fred joe bert

以上将导致java命令执行以下操作:

  1. 搜索编译版本的com.acme.example.ListUsers班级。
  2. 加载课。
  3. 检查课程是否有main方法签名 ,,,,返回类型修饰符 给出public static void main(String[])。(注意,方法参数的名称是NOT签名的一部分。)
  4. 将其传递给命令行参数(” Fred"," Joe"," Bert")为一个方法String[]

Java找不到课程的原因

当您收到消息"找不到或加载主类…“时,这意味着第一步失败了。这java命令找不到课程。确实,消息中的” …“将是完全合格的班级名称java在寻找。

那么,为什么找不到课堂呢?

原因#1-您对className参数犯了一个错误

第一个可能的原因是您可能提供了错误的班级名称。(或…正确的班级名称,但以错误的形式。)考虑到上面的示例,以下是多种多样wrong ways指定类名:

  • 示例#1-简单的类名称:

    java ListUser
    

    当课程中的班级在诸如com.acme.example,那么您必须使用完整的className包括 包装名称java命令;例如

    java com.acme.example.ListUser
    
  • 示例#2-文件名或路径名而不是类名称:

    java ListUser.class
    java com/acme/example/ListUser.class
    
  • 示例#3-带外壳不正确的类名称:

    java com.acme.example.listuser
    
  • 示例#4-错字

    java com.acme.example.mistuser
    
  • 示例#5-源文件名(Java 11或更高版本除外;请参见下文)

    java ListUser.java
    
  • 示例#6-您完全忘记了班级名称

    java lots of arguments
    

原因#2-应用程序的类路径未正确指定

第二可能的原因是班级名称是正确的,但是java命令找不到类。要了解这一点,您需要了解” classpath"的概念。这解释了出色地由Oracle文档:

因此…如果您正确指定了类名称,那么要检查的下一步是您正确指定了类路径:

  1. 阅读上面链接的三个文档。 理解至少Java类机制的基础知识。)
  2. 查看运行时有效的命令行和/或 CLASSPATH 环境变量java命令。检查目录名称和JAR文件名是否正确。
  3. 如果有相对的 classpath中的路径名,检查它们是否正确解决…从当前目录中,该目录在运行时生效java命令。
  4. 检查课程(错误消息中提到)是否可以位于有效的classpath。
  5. 请注意,类路径语法是不同的 对于Windows与Linux和Mac OS。(类路径分隔符是;在Windows和:在其他方面。如果您将错误的分离器用于平台,则将获得明确的错误消息。取而代之的是,您将在路径上获得一个不存在的文件或目录,该文件将被静默地忽略。)

原因#2a-错误目录在classpath上

当您在类路径上放置一个目录时,它在概念上对应于合格名称空间的根。类位于该根以下的目录结构中通过将完全资格的名称映射到路径名 。因此,例如,如果"/usr/usr/local/acme/class"在类路径上com.acme.example.Foon,它将在此路径名中寻找一个" .class"文件:

  /usr/local/acme/classes/com/acme/example/Foon.class

如果您将"/usr/usr/local/acme/class/com/com/acme/示例"放在classPath上,则JVM将无法找到类。

原因#2B-子目录路径与FQN不匹配

如果您的课程FQN是com.acme.example.Foon,然后JVM将在目录" com/acme/example"目录中寻找" foon.class":

  • 如果您的目录结构与上述图案不匹配包装命名,则JVM找不到您的课程。

  • 如果您尝试改名通过移动它的班级,这也将失败…但是例外的stacktrace会有所不同。有可能说这样的话:

    Caused by: java.lang.NoClassDefFoundError: <path> (wrong name: <name>)
    

    因为类文件中的FQN与类加载程序期望找到的内容不匹配。

举一个具体的例子,假设:

  • 你想跑步com.acme.example.Foon班级,
  • 完整的文件路径是/usr/local/acme/classes/com/acme/example/Foon.class,,,,
  • 您当前的工作目录是/usr/local/acme/classes/com/acme/example/,,,,

然后:

# wrong, FQN is needed
java Foon

# wrong, there is no `com/acme/example` folder in the current working directory
java com.acme.example.Foon

# wrong, similar to above
java -classpath . com.acme.example.Foon

# fine; relative classpath set
java -classpath ../../.. com.acme.example.Foon

# fine; absolute classpath set
java -classpath /usr/local/acme/classes com.acme.example.Foon

笔记:

  • -classpath可以将选项缩短为-cp在大多数Java发行中。检查各自的手动条目的java,,,,javac等等。
  • 在类路径中的绝对路径名和相对路径名之间进行选择时,请仔细考虑。请记住,如果当前目录更改,则相对路径名可能会"断开"。

原因#2C- classpath中缺少的依赖项

班级路径需要包括所有其他(非系统)您的应用程序取决于的类。(系统类是自动找到的,您几乎不需要关注自己。)为了使主要类正确加载,JVM需要查找:

(注意:JLS和JVM规格允许JVM的某些范围"懒惰"加载类,并且当抛出classLoader异常时,这可能会影响。)

原因#3-班级已在错误的软件包中声明

有时有人将源代码文件放入其源代码树中的错误文件夹中,或者他们忽略了package宣言。如果您在IDE中执行此操作,IDE的编译器将立即告诉您。同样,如果您使用不错的Java构建工具,该工具将运行javac以某种方式可以检测到问题。但是,如果您手工构建Java代码,则可以以编译器没有注意到问题的方式进行操作,并且所得的" .class"文件不在您期望的。

仍然找不到问题吗?

有很多需要检查的事情,很容易错过一些东西。尝试添加-Xdiag选项java命令行(作为之后的第一件事java)。它将输出有关班级加载的各种内容,这可能为您提供有关真正问题的线索。

另外,考虑通过网站,文档等复制和粘贴隐形或非ASCII字符引起的可能问题。并考虑"同义",其中两个字母或符号看起来相同…但不是。

如果您的签名无效或不正确,则可能会遇到此问题META-INF/*.SF。您可以尝试在您喜欢的zip编辑器中打开.jar,并从中删除文件META-INF直到你拥有的就是你MANIFEST.MF。但是,通常不建议这样做。(无效的签名可能是某人将恶意软件注入原始签名的JAR文件的结果。如果您删除无效的签名,则您正在使用恶意软件感染您的应用程序!)建议的方法是将JAR文件保留在有效签名,或从(真实的)原始源代码重建它们。

最后,如果在该问题中存在语法错误,您显然可以遇到此问题MANIFEST.MF文件(请参阅https://stackoverflow.com/a/67145190/139985)。


替代语法java

使用三种替代语法用于启动Java程序java command

  1. 用于启动"可执行" jar文件的语法如下:

    java [ <options> ] -jar <jar-file-name> [<arg> ...]
    

    例如

    java -Xmx100m -jar /usr/local/acme-example/listuser.jar fred
    

    入口点类的名称(即com.acme.example.ListUser)和类路径在JAR文件的清单中指定。

  2. 用于从模块启动应用程序(Java 9及更高版本)的应用程序的语法如下:

    java [ <options> ] --module <module>[/<mainclass>] [<arg> ...]
    

    入口点类的名称要么由<module>本身,或由可选的<mainclass>

  3. 从Java 11开始,您可以使用java命令使用以下语法来编译和运行单个源代码文件:

    java [ <options> ] <sourcefile> [<arg> ...]
    

    在哪里<sourcefile>是(通常)带有后缀" .java"的文件。

有关更多详细信息,请参阅官方文件java您正在使用的Java版本的命令。


IDES

典型的Java IDE支持在IDE JVM本身或儿童JVM中运行Java应用程序。这些都是一般来说 免受此特定例外的影响,因为IDE使用其自己的机制来构建运行时类路径,确定主类并创建java命令行。

但是,如果您在IDE背后做事,仍然有可能发生这种例外。例如,如果您以前曾在Eclipse中为Java应用程序设置了一个应用程序启动器,然后将包含"主"类的JAR文件移动到文件系统中的其他位置没有告诉日食,Eclipse会不知不觉地使用不正确的类Path启动JVM。

简而言之,如果您在IDE中遇到此问题,请检查诸如陈旧的IDE状态,损坏的项目参考或损坏的启动器配置之类的内容。

IDE也有可能简单地感到困惑。IDE是非常复杂的软件,包括许多相互作用的零件。这些部分中有许多采用各种缓存策略,以使IDE整体响应。这些有时可能会出错,并且一种可能的症状是启动应用程序时问题。如果您怀疑这可能发生,那么值得尝试其他事情,例如重新启动IDE,重建项目等等。


其他参考

来自: stackoverflow.com