“无法找到或加载主类”是什么意思?
新的 Java 开发人员遇到的一个常见问题是他们的程序无法运行,并显示错误消息:Could not find or load main class ...
这是什么意思,是什么原因造成的,你应该如何解决它?
新的 Java 开发人员遇到的一个常见问题是他们的程序无法运行,并显示错误消息:Could not find or load main class ...
这是什么意思,是什么原因造成的,你应该如何解决它?
java <class-name>
首先,您需要了解使用(或)命令启动程序的正确方法。java
javaw
正常的语法1 是这样的:
java [ <options> ] <class-name> [<arg> ...]
其中 是命令行选项(以“-”字符开头),是完全限定的 Java 类名,并且是传递给应用程序的任意命令行参数。<option>
<class-name>
<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
com.acme.example.ListUsers
main
public static void main(String[])
String[]
当您收到消息“无法找到或加载主类...”时,这意味着第一步已失败。该命令无法找到该类。事实上,“...”中将是要查找的完全限定类名。java
java
那么,为什么它可能无法找到该类呢?
第一个可能的原因是您可能提供了错误的类名。(或...正确的类名,但格式错误。考虑到上面的示例,以下是指定类名的各种错误方法:
示例 #1 - 一个简单的类名:
java ListUser
当类在诸如 的包中声明时,则必须使用完整的类名,包括命令中的包名;例如:com.acme.example
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
第二个可能的原因是类名正确,但命令找不到该类。要理解这一点,您需要了解“类路径”的概念。Oracle文档很好地解释了这一点:java
所以。。。如果已正确指定类名,则接下来要检查的是是否已正确指定类路径:
java
java
;
:
将目录放在类路径上时,它名义上对应于限定名称空间的根目录。类位于该根目录下的目录结构中,方法是将完全限定名映射到路径名。例如,如果“/usr/local/acme/classes”在类路径上,那么当JVM查找名为的类时,它将查找具有以下路径名的“.class”文件:com.acme.example.Foon
/usr/local/acme/classes/com/acme/example/Foon.class
如果你把“/usr/local/acme/classes/com/acme/example”放在类路径上,那么JVM将无法找到这个类。
如果你的类 FQN 是 ,那么 JVM 将在目录 “com/acme/example” 中查找 “Foon.class”:com.acme.example.Foon
如果您的目录结构与上述模式的包命名不匹配,则 JVM 将无法找到您的类。
如果您尝试通过移动来重命名类,那也将失败...但异常堆栈跟踪将有所不同。它很容易说这样的话:
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
javac
类路径需要包括应用程序所依赖的所有其他(非系统)类。(系统类是自动定位的,您很少需要关注这一点。要正确加载主类,JVM 需要找到:
(注意:JLS 和 JVM 规范允许 JVM“懒惰地”装入类的某些范围,这可能会影响何时引发类装入器异常。
偶尔会有人将源代码文件放入其源代码树中的错误文件夹中,或者他们省略了声明。如果在 IDE 中执行此操作,IDE 的编译器将立即告诉您有关此操作的信息。同样,如果您使用一个像样的Java构建工具,该工具将以一种检测问题的方式运行。但是,如果您手动构建Java代码,则可以以编译器不会注意到问题的方式进行,并且生成的“.class”文件不在预期的位置。package
javac
有很多东西要检查,很容易错过一些东西。尝试将该选项添加到命令行(作为之后的第一件事)。它将输出有关类加载的各种内容,这可能会为您提供有关真正问题所在的线索。-Xdiag
java
java
此外,请考虑从网站、文档等复制和粘贴不可见或非 ASCII 字符可能引起的问题。并考虑“同形异体”,其中两个字母或符号看起来相同...但事实并非如此。
如果您在 中具有无效或不正确的签名,则可能会遇到此问题。您可以尝试在自己喜欢的ZIP编辑器中打开.jar,然后删除文件,直到您拥有的只是.但是,通常不建议这样做。(无效签名可能是由于某人将恶意软件注入原始签名的 JAR 文件中。如果您删除了无效的签名,则您正在用恶意软件感染您的应用程序!推荐的方法是获取具有有效签名的 JAR 文件,或者从(真实的)原始源代码重新构建它们。META-INF/*.SF
META-INF
MANIFEST.MF
最后,如果文件中存在语法错误,您显然会遇到此问题(请参阅 https://stackoverflow.com/a/67145190/139985)。MANIFEST.MF
java
使用 启动 Java 程序有三种替代语法。java command
用于启动“可执行”JAR 文件的语法如下:
java [ <options> ] -jar <jar-file-name> [<arg> ...]
例如:
java -Xmx100m -jar /usr/local/acme-example/listuser.jar fred
入口点类(即 )的名称和类路径在 JAR 文件的 MANIFEST 中指定。com.acme.example.ListUser
从模块(Java 9 及更高版本)启动应用程序的语法如下:
java [ <options> ] --module <module>[/<mainclass>] [<arg> ...]
入口点类的名称由 其本身定义,或者由可选的 给出。<module>
<mainclass>
从 Java 11 开始,您可以使用该命令使用以下语法编译和运行单个源代码文件:java
java [ <options> ] <sourcefile> [<arg> ...]
其中(通常)是后缀为“.java”的文件。<sourcefile>
有关更多详细信息,请参阅您正在使用的 Java 发行版的命令的官方文档。java
典型的 Java IDE 支持在 IDE JVM 本身或子 JVM 中运行 Java 应用程序。这些通常不受此特定异常的影响,因为 IDE 使用自己的机制来构造运行时类路径、标识主类并创建命令行。java
但是,如果在 IDE 后面执行操作,则仍有可能发生此异常。例如,如果您之前在 Eclipse 中为 Java 应用程序设置了应用程序启动器,然后将包含“main”类的 JAR 文件移动到文件系统中的其他位置,而没有告诉 Eclipse,Eclipse 会不知不觉地使用不正确的类路径启动 JVM。
简而言之,如果您在IDE中遇到此问题,请检查过时的IDE状态,损坏的项目引用或损坏的启动器配置之类的内容。
IDE 也可能只是感到困惑。IDE是非常复杂的软件,包括许多交互部分。其中许多部分采用各种缓存策略,以使整个 IDE 具有响应性。这些有时可能会出错,一个可能的症状是启动应用程序时出现问题。如果您怀疑可能会发生这种情况,则值得尝试其他操作,例如重新启动IDE,重新生成项目等。
如果您的源代码名称是 HelloWorld.java,则编译的代码将为 。HelloWorld.class
You will get that error if you call it using:
java HelloWorld.class
Instead, use this:
java HelloWorld