javac學習-“Error: Could not find or load main class Main”
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