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

标签 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[])方法。 (请注意,方法参数的名称不是签名的一部分。)
调用该方法,将其作为String[]传递给命令行参数(“ fred”,“ joe”,“ bert”)。


Java找不到类的原因

当您收到消息“找不到或加载主类...”时,表示第一步已失败。 java命令无法找到该类。实际上,消息中的“ ...”将是java正在寻找的完全限定的类名。

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

原因#1-您在输入classname参数时犯了一个错误

第一个可能的原因是您可能提供了错误的类名。 (或者...正确的类名,但格式错误。)考虑以上示例,这里有多种错误的方法来指定类名:


Example#1-一个简单的类名:

java ListUser


当在诸如com.acme.example之类的包中声明该类时,则必须在java命令中使用完整的类名,包括包名。例如

java com.acme.example.ListUser

Example#2-文件名或路径名而不是类名:

java ListUser.class
java com/acme/example/ListUser.class

Example#3-类名的大小写不正确:

java com.acme.example.listuser

Example#4-错字

java com.acme.example.mistuser

Example#5-源文件名(Java 11或更高版本除外;请参见下文)

java ListUser.java

Example#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上是;,在其他Windows上是:。如果您在平台上使用了错误的分隔符,则不会收到明确的错误消息。相反,您将在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: <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需要查找:


班级本身。
超类层次结构中的所有类和接口(例如,参见Java class is present in classpath but startup fails with Error: Could not find or load main class
通过变量或变量声明,方法调用或字段访问表达式引用的所有类和接口。


(注意:JLS和JVM规范允许JVM在某种程度上“延迟”加载类,这可能会在引发类加载器异常时产生影响。)

原因#3-该类在错误的包中声明

偶尔会发生某人将源代码文件放入
源代码树中的文件夹错误,或者省略了package声明。如果在IDE中执行此操作,则IDE的编译器会立即告诉您有关此信息。同样,如果您使用不错的Java构建工具,则该工具将以检测问题的方式运行javac。但是,如果您手动构建Java代码,则可以通过这种方式进行处理,以使编译器不会注意到问题,并且生成的“ .class”文件不在您期望的位置。

还是找不到问题?

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

另外,请考虑由网站,文档等复制和粘贴不可见或非ASCII字符引起的可能问题。并考虑“象形文字”,如果两个字母或符号看起来相同……却不相同。

最后,如果尝试从(META-INF/*.SF)中具有错误签名的JAR文件启动,显然可以遇到此问题。



java的替代语法

使用java command启动Java程序有三种语法。

1)用于启动“可执行” JAR文件的语法如下:

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


例如

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


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

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

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


入口点类的名称由<module>本身定义,或由可选的<mainclass>给出。

3)从Java 11开始,您可以编译并运行一个源代码文件,并使用以下语法运行它:

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


在哪里(通常)是带有后缀“ .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 - “找不到或加载主类”是什么意思?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61879398/

相关文章:

java - Maven 程序集 : useStrictFiltering not available for fileSets?

java - 运行 JNLP 卡在验证应用程序上

ios - 如何确定 iOS (id) 对象的结构?

error-handling - 如何从主结果处理程序中删除默认的 “Error”

python - 使用命令行执行Python脚本

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

java - 如何保存图片并在电脑上访问它?

java - onBindViewHolder 处的 Recyclerview ClassCastException

javascript - 类方法调用作为参数存储的另一个类的方法

Java:获取类中的所有对象