“Could not find or load main class” 是什么意思?

1882

新的Java开发人员经常遇到的一个常见问题是,他们的程序无法运行,并显示以下错误信息:Could not find or load main class ...

这是什么意思,它是由什么原因引起的,以及应该如何修复它?


76
请注意,这是一个“自问自答”的问题,旨在为新Java用户提供通用参考问答。据我所知,我找不到一个已有的问答内容可以充分涵盖此问题。 - Stephen C
对我来说,这在之前工作的项目中发生了... 所以我通过“重新构建项目”来解决它。 - Vladi
1
@Vladi - “IDE也有可能会变得混乱。IDE是由许多相互作用的部分组成的极其复杂的软件。其中许多部分采用各种缓存策略,以使整个IDE响应迅速。这些策略有时会出现问题,而一个可能的症状就是启动应用程序时出现问题。如果您怀疑这种情况可能发生,请尝试其他方法,如重新启动IDE,重建项目等。” - Stephen C
@StephenC 这个问题缺乏明确和详细的描述。错误信息的引用可能是许多情况的结果。 - code4u
2
@code4u - 请查看第一个评论。显然,它缺乏任何具体问题的细节。它旨在成为一个规范的问答...而不是关于特定问题的具体问题。还要注意,主要答案涵盖了许多情况。这也是有意设计的。我认为,大约1800个赞成票和16个反对票可能意味着这个问答正在达到其预期目的。 - Stephen C
64个回答

1619

Java <类名> 命令语法

首先,您需要了解使用 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. 检查该类是否具有由public static void main(String[])给出的签名返回类型修饰符main方法。(注意,方法参数的名称不是签名的一部分。)
  4. 调用该方法,并将命令行参数("fred","joe","bert")作为String[]传递给它。

Java无法找到类的原因

当您收到"Could not find or load main class ..."的消息时,这意味着第一步失败了。java命令无法找到该类。实际上,消息中的"..."将是java正在寻找的完全限定类名

那么为什么它无法找到该类呢?

原因1 - 您在classname参数中犯了一个错误

第一个可能的原因是您可能提供了错误的类名。(或者...正确的类名,但是形式错误。)考虑上面的示例,以下是指定类名的各种错误方式:

  • 示例 #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 大量的参数
    

原因 #2 - 应用程序的类路径指定错误

第二个可能的原因是类名是正确的,但是java命令无法找到该类。要理解这一点,您需要了解"类路径"的概念。这在Oracle文档中有很好的解释:

所以...如果您已经正确指定了类名,下一步要检查的是您是否正确指定了类路径:

阅读上面链接的三个文档。(是的...要读它们!对于Java程序员来说,至少要理解Java类路径机制的基础是很重要的。)
查看在运行java命令时生效的命令行和/或CLASSPATH环境变量。检查目录名和JAR文件名是否正确。
如果类路径中有相对路径名,请检查它们是否能正确解析...从运行java命令时生效的当前目录。
检查错误消息中提到的类是否能在有效的类路径上找到。
请注意,Windows与Linux和Mac OS的类路径语法是不同的。(Windows上的类路径分隔符是“;”,而其他平台上是“:”。如果在您的平台上使用错误的分隔符,您将不会收到明确的错误消息。相反,您将得到一个不存在的文件或目录,它将被静默忽略。)
原因#2a - 类路径上有错误的目录。
当你将一个目录放在类路径上时,它在概念上对应于限定名称空间的根目录。类位于该根目录下的目录结构中,通过将完全限定名称映射到路径名来定位。例如,如果"/usr/local/acme/classes"在类路径上,那么当JVM查找名为"com.acme.example.Foon"的类时,它将在以下路径名中查找一个".class"文件:
  /usr/local/acme/classes/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将无法找到您的类。
如果您尝试通过移动类来重命名它,这也将失败...但异常堆栈跟踪将不同。它可能会显示类似于以下内容:
Caused by: java.lang.NoClassDefFoundError:

60
在尝试运行一个使用第三方库的类时,我曾遇到这个问题。我使用以下命令调用Java:java -cp ../third-party-library.jar com.my.package.MyClass;但是这种方法不起作用,需要将本地文件夹也添加到类路径中(用冒号“ : ”分隔),像这样:java -cp ../third-party-library.jar:. com.my.package.MyClass,然后它应该可以正常工作。 - lanoxx
40
在多年的Java编程之后,我仍然发现自己来到了这个页面。对我而言,问题在于类路径(classpath)语法依赖于操作系统。作为一个在Windows上编程比较新手的我并不知道这一点。 - keyser
9
额外注释,第二点救了我!看到java并没有说它找不到一个已导入的类,而是找不到你试图运行的主类,真是令人沮丧。尽管我相信这背后肯定有原因。我遇到了这种情况,java确切地知道我的类在哪里,但它无法找到其中一个已导入的类。它抱怨找不到我的主类,这实在让人恼火。 - MSX
我在Eclipse中遇到了这个问题两次。第一次是main()的签名错误。第二次是我重命名了一个.jar文件,即使我将新文件添加到构建路径中,Eclipse仍然找不到旧文件,因此项目无法编译,出现了这个错误。我不得不从“项目”>“属性”>“Java构建路径”>“库”中删除.jar文件。 - GregT
在我的情况下,使用spark2-submit时,错误语句中没有类名的Error: Could not find or load main class =错误是由于spark.properties文件错误引起的,其中我在spark.driver.extraJavaOptions -Dlog4j.configuration = log4j.properties中的等号周围有空格,该选项被声明为spark2-submit cmd行选项的一部分--files hdfs://.../log4j.properties。删除所有类似行中的空格即可解决此问题。 - codeaperature
显示剩余14条评论

301
如果您的源代码名称是HelloWorld.java,那么编译后的代码将是HelloWorld.class。如果您使用以下方式调用它,您将会得到该错误:
java HelloWorld.class

改用这个:

java HelloWorld

7
问题在于这种解决方案仅适用于默认包中声明且没有JAR文件依赖关系的Java类。(即使是这样,也不总是有效。)大多数Java程序并不那么简单。 - Stephen C
3
就像Stephen所说,这只适用于“默认包” - 这意味着文件顶部没有任何包声明。为了快速测试一些代码,我输入了以下命令:javac TestCode.java,然后是java TestCode - Someone Somewhere
这对我没有用。它仍然显示“找不到或加载主类HelloWorld”。 - Jim
28
我需要执行 java -classpath . HelloWorld - Chris Prince
2
@ChrisPrince - 是的...有时它是有效的...要理解什么情况下它有效,什么情况下无效,请阅读得票最高的答案。 - Stephen C
显示剩余5条评论

177

如果你的类在包中,那么你需要cd到项目的根目录并使用完全限定的类名(packageName.MainClassName)运行。

例如:

我的类在这里:

D:\project\com\cse\

我的主类的完全限定名是:

com.cse.Main

因此我cd回到根项目目录:

D:\project

然后发出java命令:

java com.cse.Main

这篇回答是为了帮助初学者Java程序员摆脱由一个常见错误引起的挫败感。我建议您阅读已接受的答案以获取有关Java类路径更深入的知识。


2
这个答案做了很多假设。而且还有其他的方法可以实现这个目标。我建议人们不要盲目地遵循上面的建议,而是花时间阅读我回答中解释Java类路径工作原理的链接。更好的做法是要理解你正在做什么... - Stephen C
5
这个回答符合我需要的所有假设 :) 我当时位于.class文件所在的目录,但java.exe无法运行。当我使用命令行中包含包名的方式并切换到上层目录后,它就成功运行了。 - Nick Constantine
2
我同意Nick Constantine的观点。我也是这样认为的。这个例子完全按照我所做的步骤进行,而且对我也起作用了。Java类路径处理有一定的逻辑,但我肯定不会设计成那样。 - Tihamer
谢谢,你的回答帮助我发现我使用了大写的 Main *_*! - Hernaldo Gonzalez
我在尝试后写下这条评论,但它没有起作用。我的项目名为Helloworld,只包含一个Java文件,Helloworld/src/com/firstpackage/Test.java(Windows 10,IntelliJ IDEA)。我没有CLASSPATH环境变量,我想为此项目设置classpath。在Helloworld目录中运行java com.firstpackage.Test不起作用,也不会使用命令java -classpath C:\Users\matuagkeetarp\IdeaProjects\Helloworld\src\com\first‌​package Test.java设置classpath变量。你能帮忙吗? - Prateek Gautam
@PrateekGautam - 你可能需要阅读上面的全面答案。(感谢你证明了我的观点 :-)) - Stephen C

116

使用关键字“package”

如果你的源代码中有一个package关键字(主类在一个包中定义),则应该通过层次目录运行它,使用类的完整名称(packageName.MainClassName)。

假设有一个源代码文件(Main.java):

package com.test;

public class Main {

    public static void main(String[] args) {
        System.out.println("salam 2nya\n");
    }
}

要运行此代码,您应该将Main.Class放置在类似目录的包中:

C:\ Users \ workspace \ testapp \com\test\Main.Java

然后将终端的当前目录更改为项目的根目录:

cd C:\Users\workspace\testapp

最后,运行代码:

java com.test.Main

没有关键字'package'

如果你的源代码中没有任何包(package)的名称,可能是因为你使用了错误的命令。假设你的Java文件名为Main.java,编译后:

javac Main.java

编译后的代码将会是Main.class

如果你使用以下方式调用它,你会得到那个错误:

java Main.class

改为使用这个:

java Main

1
请参阅我的答案中的“附加说明#1”,以获得更好的问题解释。 - Stephen C
17
是的,你的答案更加完整(当然,+1),但是这个特定的答案中有“package”这个词,让我能够快速找到我需要的东西。而且它起作用了。所以 +1 Razavi。StephenC,你的回答缺少我需要的简单“package”示例,因为我是Java新手。 - kmort
7
这正是我的问题。我一直在浏览大量的Java文档,而这个具体的例子就是我所需要的。 - John
1
我喜欢这个更简短、更有用的答案,而不是被接受的那一个! - Mohsen
1
请随意为您喜欢的答案点赞。这是表达您偏好的推荐方式...也是实际影响答案排名的方式。 - Stephen C
显示剩余2条评论

63

当同一份代码在一台电脑上运行正常,但在另一台电脑上出现错误时,我发现最好的解决方案是按照以下方式进行编译:

javac HelloWorld.java
java -cp . HelloWorld

3
这不是一个好的建议。你依赖于CLASSPATH环境变量未设置或者已经设置为"."这一点。虽然在许多情况下它能够工作,但在其他情况下可能会出现问题。 - Stephen C
当然,javac -classpath . HelloWorld.java 也可以工作!而且在你的情况下这是更好的解决方案。 - Stephen C
2
如果你的第一行是'package com.some.address',这将不起作用。你需要注释掉'package address'。 - Joe
1
@Joe - 那个黑客行为(注释掉包)在某些情况下可能可行,但这是一个糟糕的想法。更好的做法是学习/理解问题的原因并实施正确的解决方案。 - Stephen C
我已经正确设置了环境路径,但仍然出现“java <文件名>”错误,而“java -cp . <文件名>”则没有。如何使“java <文件名>”运行成功?@StephenC - Shubham Singhvi
显示剩余3条评论

43

在命令行上指定类路径对我有所帮助。例如:

  1. 创建一个新文件夹,C:\temp

  2. C:\temp中创建文件Temp.java,并在其中添加以下类:

     public class Temp {
         public static void main(String args[]) {
             System.out.println(args[0]);
         }
     }
    
  3. 在文件夹C:\temp中打开命令行,并输入以下命令编译Temp类:

  4.  javac Temp.java
    
  5. 运行编译的Java类,添加-classpath选项,让JRE知道在哪里找到这个类:

  6.  java -classpath C:\temp Temp Hello!
    

3
在Ubuntu中,我还必须指定路径。 不理解为什么不能默认使用当前工作目录。 我相信Java是由键盘制造商赞助的! - gone
1
@gone - "." 默认不在 $PATH 中的原因是它是一个安全陷阱。http://www.seas.upenn.edu/cets/answers/dot-path.html - Stephen C
非常感谢这个......虽然不确定为什么即使在设置环境变量后Java仍无法找到类路径。 - akash89
@akash89 - 最可能的原因是:1)java没有查看$CLASSPATH(因为您使用了-classpath或-jar),或者2)classpath设置在环境中未设置,该环境在运行java的上下文中未生效;例如,因为您没有在正确的shell中“源”添加了setenv命令的文件。 - Stephen C
我还遇到了错误: 找不到或加载主类Temp,有人能帮忙吗! - Alaa Jabre
在Windows下,使用类路径(classpath)如java -classpath c:\folder\subfoler\myCodeFolder Main也可以运行Main.class - surfmuggle

29

根据错误信息 ("Could not find or load main class"),问题可以分为两类:

  1. 找不到主类(Main class)
  2. 主类无法加载(这种情况在已接受的答案中未完全讨论)

当主类名存在错别字或语法错误,或在提供的类路径中不存在该类时,将出现找不到主类(Main class could not be found)的情况。

当类无法被初始化时,主类无法加载(Main class could not be loaded),通常情况下,主类继承了另一个类,而该类在提供的类路径中不存在。

例如:

public class YourMain extends org.apache.camel.spring.Main
如果没有包含camel-spring,将会报告此错误。

有两个原因,因为错误提示说“找不到或加载主类”。如果还有其他类别,请告诉我。我已经看到了,所以想在这里分享,也许其他人会需要它。 - Devs love ZenUML
2
我会将其修改为类似于“您需要包含所有必需的类来初始化主类,以避免出现此特定错误”的内容。我并不是要说服你,这只是我想看到的一种方式。我把答案留在这里,只是为了那些喜欢以这种方式阅读东西的人。让我们不要再延长这个讨论了 :) 我把我的陈述改为“在被接受的答案中没有完全讨论”,希望你感觉更好。 - Devs love ZenUML
6
这段信息很重要,值得明确提到(这是唯一一个提到“extends”的答案)。我刚刚通过吃亏的方式学到,当主类因为扩展其他类而找不到该类时,Java不会报告哪个实际类没有被找到(不像NoClassDefFoundError错误)。所以它确实会发生,在你不知道这一点的情况下,这是一个让人抓狂的局面。 - Hugues M.
1
在这种情况下,有没有办法准确地知道哪个依赖类未能加载? - Carlos A. Ibarra
@carlos 使用 -Xdiag 标志可能会给你一些提示。 - Oleg Kurbatov
显示剩余6条评论

23

使用这个命令:

java -cp . [PACKAGE.]CLASSNAME

示例:如果您的类名是从Hello.java创建的Hello.class,则使用以下命令:

java -cp . Hello

如果你的Hello.java文件在com.demo包内,则使用以下命令

java -cp . com.demo.Hello

在JDK 8中,往往出现类文件与java命令在同一文件夹下的情况,但是java命令需要类路径(classpath),因此我们添加-cp .选项,以当前文件夹作为类路径的参考。


这仅适用于简单情况。更复杂的情况需要更复杂的类路径。 - Stephen C
对于非常简单的情况,-cp . 是不必要的,因为如果 $CLASSPATH 未设置,则 . 是默认的类路径。 - Stephen C
通常情况下,当您安装像Websphere MQ或erlang等常见工具时,CLASSPATH会作为环境变量创建。这就是为什么提出这个问题的人可能无法编译的原因。 - shaILU
没有所谓的“那个人”。这是我编写的通用问答,旨在教育人们,让他们理解问题的原因并知道如何修复它。简单的“这样做可能会有效”的回答(依我看来)是没有帮助的,因为它鼓励人们不花时间去学习和理解。 - Stephen C
2
当我尝试从命令行运行一个简单的程序时,这对我起作用了。 - SnuKies
显示剩余3条评论

21

尝试使用-Xdiag选项。

Steve C的回答对可能出现的情况进行了很好的概述,但有时确定类是否未被找到或加载可能并不容易。可以使用java -Xdiag(自JDK 7以来)。这将打印出一个漂亮的堆栈跟踪,提供了关于Could not find or load main class错误信息含义的提示。

例如,它可以指向主类使用但未能找到并阻止了主类加载的其他类。


21

在这种情况下,我遇到了这样的错误:

java -cp lib.jar com.mypackage.Main

它在Windows中使用;,在Unix中使用:

java -cp lib.jar; com.mypackage.Main

是的。这很可能是因为您的“Main”不在JAR文件中。“-cp lib.jar;”的意思与“-cp lib.jar;。”相同,即当前目录包含在类路径中。 - Stephen C
1
最终解决了Unix的问题..谢谢(与:)一起工作) - Vicky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接