Java命令行提示找不到入口

in 积露为波 with 0 comment

初学Java时,总是要在命令行里面敲一敲、试一试才行。如果一开始就在集成开发环境中编写运行代码,很容易就会漏掉一些重要的内容。比如这个:

错误: 找不到或无法加载主类 Test
原因: java.lang.NoClassDefFoundError: a/Test (wrong name: Test)

要搞清楚这个问题,首先我们要知道Java命令行使用的一般步骤:

一般步骤:

java源代码是以 .java结尾的文件。而对于这类文件来说,它们的本质和 .txt文件的本质是相同的,即都为文本内容。但这并不意味着你可以直接编译 .txt,编译器不会允许 .txt文件通过编译,你还是需要将文件改为 *.java才行。

编译:Javac

编译就是把 .java 文件转换为 .class文件的过程,这个过程可以借用javac完成。

首先,我们需要用记事本写一个文本文件,并改名为Test.java

其内容如下:

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

要编译这个文件,我们需要运行cmd,打开命令行工具,并切换到Test.java所在目录。输入 javac Test.java

$ javac Test.java

这句话运行完成后,如果没有任何提示则说明运行正确,代码没有语法问题。

如果出现下面的情况,则是没有main入口,需要检查一下main部分是否有错误。

错误: 在类 Test 中找不到 main 方法, 请将 main 方法定义为:
   public static void main(String[] args)
否则 JavaFX 应用程序类必须扩展javafx.application.Application

运行成功后,目录下会多出一个(有时是多个) *.class文件。

在本例中,会生成一个Test.class文件

运行:java

运行就是用java虚拟机解释运行 .class字节码的过程。

如果要运行一个 class文件,我们应该以java + 包名.类名 的形式运行。

本例中没有包名(package为空),所以运行格式应该是 java + 类名。输入java Test,运行结果如下所示。

$ java Test
Hello World!

带包名的情况

再看一个略微不同的

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

这一次我们还按照原来的方法编译、运行。

c:\Users\15591\MyFiles\WorkSpace\VSCode-C
$ javac Test.java

c:\Users\15591\MyFiles\WorkSpace\VSCode-C
$ java Test
错误: 找不到或无法加载主类 Test
原因: java.lang.NoClassDefFoundError: a/Test (wrong name: Test)

命令行提示错误,那么为什么加入了package a;之后就不行了呢?

java创建起初就为了避免命名污染的问题,规定了package内容。所谓不同的package,其实就是不同的文件夹。package a 就意味着文件夹a。

此例的package为a,所以我们要将Test.class文件放到a文件夹下面才能正确运行。注意运行时把包名带上,即 a.Test

c:\Users\15591\MyFiles\WorkSpace\VSCode-C
$ mkdir a

c:\Users\15591\MyFiles\WorkSpace\VSCode-C
$ move Test.class a\
移动了         1 个文件。

c:\Users\15591\MyFiles\WorkSpace\VSCode-C
$ java a.Test
Hello World!

运行正确!

小结:

java执行class文件时,会根据输入的 包名.类名 的形式,以当前路径为基础,再根据包名所提供的相对路径信息,去该目录下寻找相应的类。

例如 ,当前目录为C:Users15591MyFiles,运行的命令为java a.b.c.D。那么java虚拟机就会去C:Users15591MyFilesabc目录下寻找D.class文件,并以它的main方法为入口开始执行。

也有朋友提到CLASSPATH的问题,但是自从jdk5.0之后,虚拟机会自动处理大部分classpath问题,我们也就不用太关注这个问题了。

Responses