Java找不到主类。

26

我已经编写了以下Java源代码文件 (Hello.java):

package com;

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello!");
    }
}

我将这个文件保存到 C:/tmpjava/Hello.java

从命令行中,我进入该目录并运行 javac Hello.java。然后我运行 dir

  • Hello.class
  • Hello.java

接着,在刚才运行 javac 的同一目录下,我运行 java Hello.class 并得到结果:

Exception in thread "main" java.lang.NoClassDefFoundError: Hello/class
Caused by: java.lang.ClassNotFoundException: Hello.class
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
Could not find the main class: Hello.class.  Program will exit.

这是怎么回事?!javac 可以正常运行,但是 java 不行?


调用时不要指定“.class”。只需键入java Hello即可。 - Thorbjørn Ravn Andersen
5个回答

53

你的类Hello属于包com。因此,你的类的完全限定名是com.Hello。当你在命令行上使用java调用程序时,应该提供包含你的main方法的类的完全限定类名,并省略.class,例如:

java com.Hello
Java程序需要完全限定的类名才能理解你所指的是哪个类。

但您还有另一个问题。Java程序使用文件系统来定位包、子包和其中所属的类。因此,如果您有一个包结构如com.Hello,Java程序期望在名为com的目录中找到一个名为Hello.class的类文件,就像这样:com/Hello.class。实际上,您可以在看到的Exception中观察到此行为;您错误地使用了Hello.class,它被解释为名为Hellopackage和名为classclass,并正在查找目录结构Hello/class

java.lang.NoClassDefFoundError: Hello/class

但编译器javac默认情况下不设置此目录结构。请参见javac文档,但重要的是这一点:在进行编译时,可以使用-d标志指定目标目录:

-d directory

设置类文件的目标目录。目标目录必须已经存在;javac不会创建目标目录。如果一个类是包的一部分,javac将该类文件放入反映包名的子目录中,并根据需要创建目录。例如,如果您指定-d c:\myclasses并且类名为com.mypackage.MyClass,则类文件称为c:\myclasses\com\mypackage\MyClass.class

如果未指定-d,javac会将类文件放在与源文件相同的目录中。

粗体字部分是初学者常见的混淆点,也是你自己问题的一部分。

所以您有两个选择:

  1. 在您的情况下,如果您将当前目录作为目标目录提供,则可以正常工作,如下所示(句点.表示当前目录):

    javac -d . Hello.java
    
    如果您像这样调用编译器,它将为您创建“com”目录,并将编译后的类文件放入其中,以便Java程序可以在运行时找到它。然后当您像上面那样从"c:\tmpJava"运行java时,您的程序应该会执行。 您可以使用反映包结构的目录结构设置源代码:将源文件"Hello.java"放在名为"com"的目录中,例如: "c:\tmpJava\com\Hello.java"。现在,您可以从"c:\tmpJava"运行javac编译,就像这样:
    javac com\Hello.java
    

    你没有提供-d标志,但这没关系,因为你已经自己创建了目录结构,引用上面的文档:

    如果未指定 -d,则 javac 将类文件放在与源文件相同的目录中。

    同样,当你像上面那样运行 java 时,程序应该会执行。

    请注意,这是 Java 程序员常用的第二种方法:将源代码文件组织成与包结构相对应的目录结构。

    在这个解释中,我们忽略了 classpath 的概念。你也需要理解这一点才能编写 Java 程序,但在当前目录下仅编译一个程序的情况下,如果按照上述两种方法之一编译类,则可以省去设置 classpath 的步骤,因为默认情况下,Java 程序将当前目录作为 classpath。这是来自于Java 文档的另一段引用:

      

    -cp classpath

         

    指定要搜索类文件的目录、JAR 文件和 ZIP 文件列表。类路径的条目由分号(;)分隔。指定 -classpath 或 -cp 将覆盖 CLASSPATH 环境变量的任何设置。

         

    如果未使用 -classpath 和 -cp,并且未设置 CLASSPATH,则用户类路径包括当前目录(.)。

    请注意,当你使用像 Eclipse 这样的 IDE 运行 Java 代码时,它大部分都会为你处理好,但你仍然会遇到 classpath 问题。


很棒的回答。虽然我知道这些概念,但这是一个很好的复习。 - Rishi Dua
谢谢!我正在学习Java,自己想着:“嘿,Eclipse给了我太多的东西,让我们也学习一下基础知识”,这很完美,可以理解为什么我不能在命令行上运行我的程序。 - Gauntlet
@Gauntlet 很高兴能帮到你:自 Java 诞生以来,这一直令人困惑。关于 CLASSPATH,仍有很多需要注意的地方,你可能会发现自己需要回头多次审查细节。此外,如果这个答案中有任何不清楚或明显错误的地方,请编辑(或评论):让新鲜的眼睛来解释总是好的。 - pb2q
非常好的答案。作为一名.NET程序员,我已经很久没有接触Java了,这个问题真的让我困扰了很久。谢谢。 - sovemp
@sovemp 没关系,很高兴看到旧答案仍然有帮助。 - pb2q
@sovemp,既然你来自.NET,我还建议如果你没有使用可以为你管理这个的IDE,你也应该深入了解设置_classpath_的细节,特别是如果你将在IDE之外设置一个非平凡的项目/构建结构。我已经有一段时间没有做过这件事了,但你应该能够在Windows中将其设置为环境变量。 - pb2q

16
Java命令的语法如下:
java [classname]

java [filename]

Java会在其类路径中寻找您所提供名称的类。通常,类路径包括当前目录,因此:

java Hello

如果Hello.class文件在当前目录中,可以直接执行其中的main()方法。

但是,如果这个类在其他地方(比如一个.jar包或者系统的其他目录)则需要通过设置CLASSPATH环境变量或者在命令行中指定它的位置。

java -cp build/classes Hello
java -cp build/jars/myjar.jar Hello

无效的赞同,无法撤销,因为它实际上也不可踩。哎呀。 - KevinDTimm
1
如果您第二次点击向上箭头,它会取消您的点赞(我很高尚地指出这一点!) - slim
不,我会放弃它 :) - KevinDTimm

4

该类应该位于 C:\tmpjava\com\Hello.class
并且您应该从 C:\tmpjava 运行: java -cp . com.Hello
当您将一个类放在一个包中时,它定义了该类的文件结构。即,您的类在包 com 中,应该在文件夹 com 中。


2
运行以下命令:
java -cp . com.Hello

1

java -classpath c:\tmpjava com.Hello

Java -classpath c:\tmpjava com.Hello


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