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

标签 java class main

新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命令执行以下操作:
  • 搜索com.acme.example.ListUsers类的编译版本。
  • 加载类。
  • 检查类是否具有main方法,该方法具有public static void main(String[])给出的签名,返回类型和修饰符。 (注意,方法参数的名称是,不是签名的部分。)
  • 调用该方法,将命令行参数(“fred”,“joe”,“bert”)作为String[]传递给该方法。

  • Java找不到类的原因
    当您收到消息“找不到或加载主类...”时,表示第一步已失败。 java命令无法找到该类。实际上,消息中的“...”将是java寻找的完全限定的类名。
    那么为什么找不到类(class)呢?
    原因#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 lots of arguments
    

  • 原因#2-应用程序的类路径指定不正确
    第二个可能的原因是类名正确,但是java命令找不到该类。要了解这一点,您需要了解“类路径”的概念。 Oracle文档对此进行了很好的解释:
  • The java command documentation
  • Setting the Classpath
  • Java教程-PATH and CLASSPATH

  • 所以...如果您正确指定了类名,那么接下来要检查的是您正确指定了类路径:
  • 阅读上面链接的三个文档。 (是的,请阅读它们!Java程序员至少要了解Java类路径机制的基本原理,这一点很重要。)
  • 查看命令行和/或运行java命令时有效的CLASSPATH环境变量。检查目录名和JAR文件名是否正确。
  • 如果类路径中有相对路径名,请从运行java命令时有效的当前目录中检查它们是否正确解析。
  • 检查类(在错误消息中提到)是否可以位于有效的类路径上。
  • 请注意,Windows与Linux和Mac OS的类路径语法不同。 (在Windows上,类路径分隔符是;,在其他上是:。如果为平台使用错误的分隔符,则不会收到明确的错误消息。相反,在路径上将存在不存在的文件或目录默默地忽略。)

  • 原因#2a-错误的目录位于类路径中
    将目录放在类路径上时,它在概念上对应于限定 namespace 的根。通过将完全限定的名称映射到路径名,类位于该根目录下的目录结构中。因此,例如,如果“/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将找不到您的类。
  • 如果尝试通过移动某个类来重命名该类,则该操作也会失败...但是异常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
    
    笔记:
  • 在大多数Java版本中,-classpath选项可以缩写为-cp。检查相应的手册条目中的javajavac等。
  • 在类路径中的绝对路径名和相对路径名之间进行选择时,请仔细考虑。请记住,如果当前目录更改,则相对路径名可能会“中断”。

  • 原因#2c-类路径中缺少依赖项
    类路径需要包括应用程序依赖的所有其他(非系统)类。 (系统类是自动定位的,您几乎不必为此担心。)为了正确加载主类,JVM需要查找:
  • 类本身。
  • 父类(super class)层次结构中的所有类和接口(interface)(例如,参见Java class is present in classpath but startup fails with Error: Could not find or load main class)
  • 通过变量或变量声明,方法调用或字段访问表达式引用的所有类和接口(interface)。

  • (注意:JLS和JVM规范允许JVM在某种程度上“延迟”加载类,这可能会在引发类加载器异常时产生影响。)
    原因#3-该类已在错误的包中声明
    偶尔会发生某人将源代码文件放入
    源代码树中的文件夹错误,或者省略了package声明。如果您在IDE中执行此操作,则IDE的编译器会立即告诉您有关此信息。同样,如果您使用不错的Java构建工具,则该工具将以检测问题的方式运行javac。但是,如果您手动构建Java代码,则可以通过这种方式进行处理,以使编译器不会注意到问题,并且生成的“.class”文件不在您期望的位置。
    还是找不到问题?
    有很多东西要检查,很容易错过一些东西。尝试将-Xdiag选项添加到java命令行(作为java之后的第一件事)。它将输出有关类加载的各种信息,这可能会为您提供真正问题的线索。
    另外,请考虑由网站,文档等复制和粘贴不可见或非ASCII字符引起的可能问题。考虑“象形文字”,其中两个字母或符号看起来相同...但不是。
    最后,如果尝试从(META-INF/*.SF)中具有错误签名的JAR文件启动,显然可以遇到此问题。
    java的替代语法
    使用java command启动Java程序有三种语法。
  • 用于启动“可执行” JAR文件的语法如下:
    java [] -jar [...]

  • 例如
      java -Xmx100m -jar /usr/local/acme-example/listuser.jar fred
    
    入口点类的名称(即com.acme.example.ListUser)和类路径在JAR文件的MANIFEST中指定。
  • 从模块(Java 9和更高版本)启动应用程序的语法如下:
    java [] --module [/] [...]

  • 入口点类的名称由<module>本身定义,或由可选的<mainclass>给出。
  • 从Java 11开始,您可以编译并运行一个源代码文件,并使用以下语法运行它:
    java [] [...]

  • 在哪里(通常)是带有后缀“.java”的文件。
    有关更多详细信息,请参阅所用Java版本的java命令的官方文档。

    集成开发环境
    典型的Java IDE支持在IDE JVM本身或子JVM中运行Java应用程序。这些通常不受此特定异常的影响,因为IDE使用自己的机制来构造运行时类路径,标识主类并创建java命令行。
    但是,如果您在IDE背后进行操作,则仍然可能发生此异常。例如,如果您先前已在Eclipse中为Java应用程序设置了应用程序启动器,然后在不告知Eclipse的情况下将包含“主”类的JAR文件移动到了文件系统中的其他位置,则Eclipse会无意间启动JVM。具有不正确的类路径。
    简而言之,如果您在IDE中遇到此问题,请检查IDE的状态是否陈旧,项目引用损坏或启动器配置损坏。
    IDE也可能会很容易混淆。 IDE是非常复杂的软件,包含许多交互部分。这些部分中的许多部分都采用各种缓存策略,以使IDE整体具有响应能力。这些有时可能会出错,并且一种可能的症状是启动应用程序时出现问题。如果您怀疑这可能会发生,那么值得尝试其他事情,例如重新启动IDE,重建项目等等。

    其他引用

    Oracle Java教程中的
  • -Common Problems (and Their Solutions)
  • 关于java - “Could not find or load main class”是什么意思?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41132752/

    相关文章:

    java - 如何绕过 ArrayList.contains 使用 ==

    c++ - 继承和派生类

    PHP:可以在通过ajax加载页面后访问类吗?

    java - “找不到或加载主类”是什么意思?

    java - “找不到或加载主类”是什么意思?

    c - 退出状态是可观察的行为吗?

    java - 循环群生成器

    java - 省略数字方法错误

    java - 检测动画结束(Android Studio)

    c++ - 返回对类成员容器的元素的引用