javac学习-“Error: Could not find or load main class Main”

一、上报错代码

package hello;

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

编译后,运行报错

$ javac HelloWorld.java
$ java HelloWorld
Error: Could not find or load main class HelloWorld

二、查找问题

2.1 查找java入门的代码示例

百思不得其解,到在线学习java的网站上看了下代码,我的代码只是多了package,难道是这个产生的这个问题吗?

示例代码

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}
$ javac HelloWorld.java
$ java HelloWorld
Hello World

也给网站上加一个package,一样报错。

Error: Could not find or load main class Main
Caused by: java.lang.NoClassDefFoundError: hello/Main (wrong name: Main)

Exited with error status 1
2.2 csdn文章大佬分享

在一个csdn的帖子里说到了classpath的问题,但是没有说透,写的太繁琐了。

三、fire in the hole

我大概明白了我的代码运行错误的原因了。常常用idea intellij搬砖,已经忘记了来的路了。其实intellij软件会根据我们命名的package,建立对应的目录,包括编译出的class文件的路径也是按照这个结构。

对照着分析着裸奔的java代码和工程里的java文件路径,猜测java在执行代码的时候,package和目录应该这都是要一一对应的。查看了大佬关于jvm中的说明,并提到了“双亲委派机制”中的过程。jvm中照这个package,转换成目录来查找class的

classpath只是一个基点,package是以classpath的相对路径。

以下是URLClassLoader.java中的部分代码。

/* The search path for classes and resources */
private final URLClassPath ucp;

protected Class<?> findClass(final String name)
  throws ClassNotFoundException
{
  final Class<?> result;
  try {
    result = AccessController.doPrivileged(
      new PrivilegedExceptionAction<Class<?>>() {
        public Class<?> run() throws ClassNotFoundException {
          String path = name.replace('.', '/').concat(".class");
          Resource res = ucp.getResource(path, false);
          if (res != null) {
            try {
              return defineClass(name, res);
            } catch (IOException e) {
              throw new ClassNotFoundException(name, e);
            }
          } else {
            return null;
          }
        }
      }, acc);
  } catch (java.security.PrivilegedActionException pae) {
    throw (ClassNotFoundException) pae.getException();
  }
  if (result == null) {
    throw new ClassNotFoundException(name);
  }
  return result;
}

四、测试

于是我新建了hello目录,将HelloWorld.java扔入其中,不进去hello目录,执行编译和运行的命令。

$ mkdir hello
$ mv HelloWorld.java hello
$ javac hello/HelloWorld.java 
$ java hello.HelloWorld
Hello World

$ rm HelloWorld.class 
$ java hello.HelloWorld
Hello World

$ java hello/HelloWorld
Hello World

结果显示正确。

打算继续测试-cp和package的关系,进入hello目录,再试了一下

$ cd hello/
$ java HelloWorld
Error: Could not find or load main class HelloWorld

$ java -cp ../ hello/HelloWorld
Hello World

$ javac HelloWorld.java 
$ java -cp ../ hello/HelloWorld
Hello World

$ java -cp ./ hello/HelloWorld
Error: Could not find or load main class hello.HelloWorld

$ java HelloWorld
Error: Could not find or load main class HelloWorld

五、结论

通过以上测试,当代码中有指定package时,有几点需要注意的:

5.1 目录结构和package要对应

目录结构和package要对应,如 package hello,就一定更要把class文件放在hello目录下

5.2 在运行的时候一定要带上全称:package + 类名

如:hello/HelloWorld 或 hello.HelloWorld

5.3 classpath路径问题

classpath不指定,默认表示是当前文件夹,在有package的情况下,一定要可以通过classpth + package找到你要执行的class-cp表示指定classpath路径

(1)如果和class文件在同一目录,那么就要把classpath改写到上一级目录:

java -cp …/ hello/HelloWorld

(2)如果和class文件同一个目录,可以

java -cp ./ hello/HelloWorld

(3)并且,因为是同一目录,可以省略-cp,

java hello/HelloWorld