【問題記錄】找不到或無法加載主類HelloWorld

背景

在寫《【java基礎(四)】hello, world》時,無意遇到的一個問題,對於一個老程序猿在hello, world中遇到的問題卻想不出來一個所以然,所以專門補了補功課。

問題描述

使用記事本編程hello, world,手動javac編譯,java執行。在未使用package時一切正常,使用package後,一下懵了。

問題重現

  • 使用記事本編程hello, world

源碼:

public class HelloWorld {

	public static void main(String[] args) {
		System.out.println("hello, world");
	}

}
  • 使用javac編譯,java執行,一切正常。

  • 在源碼上使用package。

源碼:

package hello;

public class HelloWorld {

	public static void main(String[] args) {
		System.out.println("hello, world");
	}

}
  • 使用javac編譯,java執行,錯誤重現。

問題解決

從問題現象上來看,很明顯可以看出是package的問題,但是我卻不明白問題出在哪裏,於是尋求答案。

參考文章:解決dos窗口下運行.class文件出現錯誤: 找不到或無法加載主類 HelloWorld

  • 解決方法一

去掉package。

  • 解決方法二

使用javac -d . HelloWorld.java編譯。

使用java hello.HelloWorld執行。

javac -d . HelloWorld.java
java hello.HelloWorld

深度學習

雖說問題可以解決,但最終還是歸咎與不理解package關鍵字。於是對package進行補課學習。

參考文章:java初學者,如何理解package和import?

  • 以下是對文章的關鍵部分的引用(重點在第5條):

作者:楓亦
鏈接:https://www.zhihu.com/question/278555424/answer/667214584
來源:知乎
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

1.java文件通過javac編譯工具編譯變成 class文件。

2.java執行工具可以執行class文件。

3.sourcepath定義了源碼文件搜索的“根路徑”,可以設置多條根路徑。

4.classpath定義了class文件搜索的“根路徑”,可以設置多條根路徑。

5.package關鍵字指明當前源文件和此文件被編譯成class後的文件所在“相對路徑”, package和電腦文件系統的目錄其實是一個意思,比如 com.xx.yy 對應 Linux的 com/xx/yy 對應windows的com\xx\yy。但是注意一個問題,這個路徑是相對的,因爲沒有根,根需要從sourcepath或classpath裏面去找。

 比如根 classpath = D:\A;D:\B。
 那麼完整的物理路徑就可能是 D:\A\com\xx\yy 也可能是 D:\B\com\xx\yy
 所以同屬一個package的源碼文件也即開頭都用了 package com.xx.yy 的源碼文件不一定非要在同一個物理目錄下面。

6.javac編譯過過程中遇到import語句,就會根據 “classpath + 相對路徑”去找 class文件,根據“sourcepath+相對路徑“去找源文件。規則是這樣的:

  • 如果只有class文件,就直接用class文件。
  • 如果只有源文件,就把源文件編譯成class文件。
  • 如果既有class文件又有源文件,就會檢查文件修改的時間戳,如果class文件晚於源文件,就直接用class文件,如果早於源文件,就重新編譯源文件。

另外關於找到的標準要滿足兩條:

  • 文件名必須跟類名一樣。
  • 文件裏面package定義的相對路徑必須和import定義的相對路徑一樣。

7.簡單說一下class文件,前面提到的相對路徑信息在源碼中是package關鍵字指明的,class文件中也有這些信息,只不過不是在文件頭部指明,而是直接內化成類全稱了,看個例子:

源碼:
package A;
public class Hello{
  public static void main(String args[]){   
     System.out.println("Hello World!");
  }
}

class文件 反編譯效果:
public class A.Hello {
  public A.Hello();
  public static void main(java.lang.String[]);
}

注意 Hello類直接變成了 A.Hello,也就是類名稱自動包含了路徑信息。

8.java執行class文件時,遇到 如new、getstatic 等等需要創建類實例的相關指令時,就會加載相關的class文件(也稱動態加載),那麼jvm會去哪裏找class文件呢?跟javac類似還是通過“classpath + 相對路徑”去找。

9.jar文件就是把class文件以及相對目錄結構打包而成的文件,感興趣可以直接解壓看看。

思考

跟同年出道的朋友討論此問題,發現對package一樣的懵。我感覺產生這種問題的原因是:

  • 太過於依賴IDE。這也不能是一種錯誤,畢竟IDE帶來的便利性是沒有辦法替代的。
  • 基礎不紮實。很多問題都不喜歡追究溯源,只是到了解決問題的步驟就可以了。
  • 意想(瞎想)問題原因。我感覺這是現在人們的通病,自己本身不知道問題出在那裏,單純的根據現象猜測出現問題的原因,並且說的理直氣壯,其實根本沒有理論的支撐。就好像我朋友說這個問題是由於“引入了不存在的包”,這種“不存在的包”這種詞語就屬於意想出來的。
  • 不理解本質。想package的感念,很多人都理解爲和文件目錄一樣,我感覺你可以這樣理解package,但不可以把package和文件目錄劃等號,更好的理解應該是命名空間(本人能力有限,嘴皮也有限,不多陳述)。

捐贈

若你感覺讀到這篇文章對你有啓發,能引起你的思考。請不要吝嗇你的錢包,你的任何打賞或者捐贈都是對我莫大的鼓勵。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章