新的 Java 开发人员遇到的一个常见问题是他们的程序无法运行并显示错误消息: Could not find or load main class ...
这是什么意思,是什么原因造成的,您应该如何解决?
原文由 Stephen C 发布,翻译遵循 CC BY-SA 4.0 许可协议
新的 Java 开发人员遇到的一个常见问题是他们的程序无法运行并显示错误消息: Could not find or load main class ...
这是什么意思,是什么原因造成的,您应该如何解决?
原文由 Stephen C 发布,翻译遵循 CC BY-SA 4.0 许可协议
15 回答8.4k 阅读
8 回答6.2k 阅读
1 回答4k 阅读✓ 已解决
3 回答6k 阅读
3 回答2.2k 阅读✓ 已解决
2 回答3.1k 阅读
2 回答3.8k 阅读
java <class-name>
命令语法首先,您需要了解使用
java
(或javaw
)命令启动程序的正确方法。正常的语法1是这样的:
其中
<option>
是命令行选项(以“-”字符开头),<class-name>
是完全限定的 Java 类名,<arg>
是任意命令传递给您的应用程序的行参数。1 - 在这个答案的末尾附近描述了一些其他语法。
类的完全限定名 (FQN) 按照惯例编写,就像在 Java 源代码中一样;例如
但是,某些版本的
java
命令允许您使用斜线而不是句号;例如这(令人困惑地)看起来像一个文件路径名,但不是一个。请注意,术语 完全限定名称 是标准的 Java 术语……不是我编造的让你感到困惑的东西:-)
这是
java
命令的示例:以上将导致
java
命令执行以下操作:com.acme.example.ListUsers
类的编译版本。main
方法,其 _签名_、 返回类型 和 修饰符 由public static void main(String[])
给出。 (注意,方法参数的名称 不是 签名的一部分。)String[]
传递给它。Java找不到类的原因
当您收到消息“Could not find or load main class …”时,这意味着第一步失败了。
java
命令无法找到类。事实上,消息中的“…”将是java
正在寻找的 _完全限定类名_。那么为什么它可能找不到类呢?
原因 #1 - 你在类名参数上犯了一个错误
第一个可能的原因是您可能提供了错误的类名。 (或者…正确的类名,但格式不对。)考虑上面的例子,这里有多种指定类名的 错误方法:
当类在诸如
com.acme.example
的包中声明时,则必须在java
命令中使用 包含 包名称的完整类名;例如原因 #2 - 应用程序的类路径指定不正确
第二个可能的原因是类名是正确的,但是
java
命令找不到类。要理解这一点,您需要理解“类路径”的概念。 Oracle 文档对此进行了 很好 的解释:java
命令文档所以…如果您正确指定了类名,接下来要检查的是您是否正确指定了类路径:
java
命令时生效的命令行和/或 CLASSPATH 环境变量。检查目录名和 JAR 文件名是否正确。java
命令时生效的当前目录。;
:
其他平台上是 —。如果你为你的平台使用了错误的分隔符,你不会得到明确的错误消息。相反,你会在将被静默忽略的路径上获取不存在的文件或目录。)原因 #2a - 类路径上有错误的目录
当您将一个目录放在类路径中时,它在理论上对应于限定名称空间的根目录。 _通过将完全限定名称映射到路径名_,类位于该根下的目录结构中。因此,例如,如果“/usr/local/acme/classes”在类路径上,那么当 JVM 查找名为
com.acme.example.Foon
的类时,它将查找一个“.class”文件路径名:如果您将“/usr/local/acme/classes/com/acme/example”放在类路径中,那么 JVM 将无法找到该类。
原因 #2b - 子目录路径与 FQN 不匹配
如果您的类 FQN 是
com.acme.example.Foon
,那么 JVM 将在目录“com/acme/example”中寻找“Foon.class”:如果您的目录结构与上述模式中的包命名不匹配,JVM 将找不到您的类。
如果你试图通过移动它来 重命名 一个类,那也会失败……但异常堆栈跟踪会有所不同。它可能会说这样的话:
因为类文件中的 FQN 与类加载器期望找到的内容不匹配。
举一个具体的例子,假设:
com.acme.example.Foon
类,/usr/local/acme/classes/com/acme/example/Foon.class
,/usr/local/acme/classes/com/acme/example/
,然后:
笔记:
-classpath
选项可以缩短为-cp
。检查java
、javac
等的相应手册条目。原因 #2c - 类路径中缺少依赖项
类路径需要包含您的应用程序所依赖的所有 _其他_(非系统)类。 (系统类是自动定位的,你很少需要关心这个。)为了正确加载主类,JVM 需要找到:
(注意:JLS 和 JVM 规范允许 JVM 在一定范围内“延迟”加载类,这会影响何时抛出类加载器异常。)
原因 #3 - 该类已在错误的包中声明
偶尔会发生有人将源代码文件放入其源代码树中的错误文件夹中,或者他们遗漏了
package
声明。如果您在 IDE 中执行此操作,IDE 的编译器将立即告诉您。同样,如果您使用合适的 Java 构建工具,该工具将以检测问题的方式运行javac
。但是,如果您手动构建 Java 代码,编译器可能不会注意到问题,并且生成的“.class”文件不在您期望的位置。还是找不到问题?
有很多东西要检查,很容易漏掉一些东西。尝试将
-Xdiag
选项添加到java
命令行(作为java
之后的第一件事)。它将输出有关类加载的各种信息,这可能会为您提供有关真正问题所在的线索。此外,请考虑从网站、文档等复制和粘贴不可见或非 ASCII 字符可能导致的问题。并考虑“homoglyphs”,其中两个字母或符号看起来相同……但不是。
如果您在
META-INF/*.SF
中有无效或不正确的签名,您可能会遇到此问题。您可以尝试在您最喜欢的 ZIP 编辑器中打开 .jar,并从META-INF
中删除文件,直到您只剩下MANIFEST.MF
。但是,一般不推荐这样做。 (无效签名可能是有人将恶意软件注入原始签名的 JAR 文件的结果。如果您删除无效签名,您将用恶意软件感染您的应用程序!)推荐的方法是获取具有有效签名的 JAR 文件签名,或从(真实的)原始源代码重建它们。最后,如果
MANIFEST.MF
文件中存在语法错误,您显然会遇到此问题(请参阅 https://stackoverflow.com/a/67145190/139985 )。java
使用
java command
启动 Java 程序有三种替代语法。例如
入口类的名称(即
com.acme.example.ListUser
)和类路径在 JAR 文件的清单中指定。入口点类的名称要么由
<module>
本身定义,要么由可选的<mainclass>
给出。java
命令使用以下语法编译和运行单个源代码文件:其中
<sourcefile>
是(通常)后缀为“.java”的文件。有关详细信息,请参阅您正在使用的 Java 版本的
java
命令的官方文档。集成开发环境
典型的 Java IDE 支持在 IDE JVM 本身或子 JVM 中运行 Java 应用程序。这些 通常 不受此特定异常的影响,因为 IDE 使用自己的机制来构建运行时类路径、识别主类并创建
java
命令行。但是,如果您在 IDE 背后进行操作,则仍有可能发生此异常。例如,如果您之前在 Eclipse 中为您的 Java 应用程序设置了一个应用程序启动器,然后您在 没有告知 Eclipse 的情况下将包含“主”类的 JAR 文件移动到文件系统中的其他位置,Eclipse 将无意中启动 JVM使用不正确的类路径。
简而言之,如果您在 IDE 中遇到此问题,请检查是否存在陈旧的 IDE 状态、损坏的项目引用或损坏的启动器配置等问题。
IDE 也有可能简单地感到困惑。 IDE 是非常复杂的软件,包含许多交互部分。其中许多部分采用各种缓存策略,以使 IDE 作为一个整体响应。这些有时会出错,一种可能的症状是启动应用程序时出现问题。如果您怀疑可能会发生这种情况,则值得尝试其他操作,例如重新启动 IDE、重建项目等。
其他参考资料