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的问题,但是没有说透,写的太繁琐了。但是给的一个链接对classpath的介绍说的很好。
csdn 帖子:https://blog.csdn.net/abcdu1/article/details/86693800
stackover帖子:https://stackoverflow.com/questions/18093928/what-does-could-not-find-or-load-main-class-mean
这个stackover帖子很清晰的罗列了包罗万象的java编译出错的原因。
答案可能在其中,但是我没有得到很清晰的答案,继续上下而求索。
三、fire in the hole
最后在百度的知道里找到很直接很暴力的答案 (百度知道很难得提供了正确的信息)。
https://zhidao.baidu.com/question/213065551.html
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b2dyJNQ0-1607748802719)(…/image/image-20201212115120286.png)]
这里清晰的说出了,如果有package,那么你的java文件所在的目录结构就要和package对应。
我大概明白了我的代码运行错误的原因了。常常用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