淺談為什么java命令運行class文件出現異常

笨的舍 7年前發布 | 30K 次閱讀 Java Java開發

摘要

相信每一個初學者學習Java時,剛開始練習編程Java都會寫一個HelloWord程序,然后會按照書本上那樣用命令行去編譯,解釋Java程序,或許當時你執行成功了;可是當你進一步學習更多的知識的時候,學到了用包命名空間來解決類名沖突后,你寫的程序會更加完善健壯,而你此時還是利用初學時的方法去編譯解釋這個程序錯誤時,可能會遇到這個錯誤: Exception in thread “main” java.lang.NoClassDefFoundError: 。

今天呢我就淺談一下對于這個異常的發生的原因和解決方案。我相信你看過之后會更加理解包在Java中的存在的意義!

舉例

源程序如下:

package com.study.mengyi.access;
/**

  • 用于測試在dos下利用java命令行的形式運行程序
  • @ClassName TestDosRunning
  • @Description
  • @Author Meng Yi
  • @Date 2017年7月18日 上午11:01:37 */ public class TestDosRunning { public static void main(String[] args){
     System.out.println("Code is running!");
    
    } }</code></pre>

    當然你如果用IDE來運行程序,就不會出現開頭出現的異常,因為開發工具已經幫你解決好了,這里先不細說,接下來我們在dos下面cd到源文件的路徑下,用javac 編譯這個源程序。

    如上圖,若javac 命令后沒有出現報錯,說明我們的路徑正確,編譯成功,之后我們一般都會直接在當前目錄下進行 java class文件(class文件為編譯后的文件),例如本例 java TestDosRunning ,然后當我們敲下這個命令運行后,發現會報錯!

    Exception in thread "main" java.lang.NoClassDefFoundError: TestDosRunning (wrong name: com/study/mengyi/access/TestDosRunning)
    
     at java.lang.ClassLoader.defineClass1(Native Method)
     at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
     at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
     at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
     at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
     at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
     at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
     at java.security.AccessController.doPrivileged(Native Method)
     at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
     at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
     at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
     at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:482)</code></pre> 
    

    上述異常的說的是:在主線程中存在一個異常:沒有找到要運行的類文件(即class文件)。Wait,我就是在當前目錄下進行運行的啊,文件名也對啊(注意大小寫),怎么會找不到呢? 百度一下吧,網上說的亂七八糟,密密麻麻的說了一堆了,你覺得如果是一個新手,他能認真看嘛,為什么不用自己話來解答這個問題呢?

    分析

    既然報錯說的是類文件沒有找到,所以我們應該第一時間想到的是ClassPath**類加載路徑**,該變量指示JVM在執行會在這個路徑上面找到我們要運行的class文件,在之前呢,必須手動設置ClassPath,但是對于新手來說,設立這個無異是件麻煩的事情,所以Sun公司將Java2中JDk改造了更加聰明了一點,就是我們在設置好Java運行環境后,就不用再設置ClassPath變量,JDK會智能的找到類加載路徑。而且默認的classpath會從當前目錄中尋找class文件,這樣說來,類加載的路徑是對的。所以排除!

    難道我們的訪問方式有問題?是的。首先看這個例子:

    /**
  • 用于測試在dos下利用java命令行的形式運行程序
  • @ClassName TestDosRunning
  • @Description
  • @Author Meng Yi
  • @Date 2017年7月18日 上午11:01:37 */ public class TestDosRunning { public static void main(String[] args){
     System.out.println("Code is running!");
    
    } }</code></pre>

    What?這個怎么行? 仔細對比與之前例子發現這個程序沒有寫包名 ,這也是為什么我剛學Java時,dos可以運行,而我用包之后怎么就不行了?

    我們先說下什么包? 包是Java中為了解決命名沖突而設置的,包內含有一組類,這些類共同組成類庫,包的命名方式采用域名倒著寫,因為域名在全網是唯一的,所以這樣我們創建的包名也就是唯一的,所以就不會存在類命名沖突問題。 這樣就導致了一個 類的在JVM中必須是全類名,即包含前綴包名,這樣解析器就可以正確的加載類,而不會出現歧義,所以我們在用java 執行命令時必須指定類的全類名。

    不寫包名的話,意味著這是默認包,虛擬機會把所有的不寫包名的類規整在一起組成默認包,所以不寫包名情況的下,java TestDosRunning可以被解釋器識別并執行。

    好了既然,我們理解了這樣的方式,我們就用最開始的含包的程序檢驗一下是不是這個理,let’s go!

    what?我按全類名來運行了,怎么還不行啊?

    上面說到了包名采用了域名倒置的寫法,這樣的做法就是要做到有層次,雖然包名能唯一表示一個類,但是類文件始終都是要存儲在操作系統中,而在操作系統是就是靠文件目錄結構來解決命名沖突的,通過目錄層次分劃即可區分開不同的類,所以package名稱被分解為機器上的一個目錄。

    Java解析器的運行過程就是:首先,從classpath環境變量中類的各個存放路徑,它們都為class文件的根目錄。從根目錄開始,解釋器根據類的包名將轉換成路徑,比如 com.study.mengyi.access; 會被轉換成根目錄/com/study/mengyi/access,最后從access中讀取到要加載的類文件。

    方式一

    所以上面我們直接在D:\JavaLearning\ThinkInJava\src\com\study\mengyi\access目錄下進行 java TestDosRunning ,解析器會在access中尋找/com/study/mengyi/access,然而并沒有這個目錄,所以我們來手動生成一個這樣的目錄結構并把class文件復制過來檢驗一下!

    最終class文件的路勁為:

    D:\JavaLearning\ThinkInJava\src\com\study\mengyi\access\com\study\mengyi\access

    運行成功!果然是這樣,是不是一下子就對這個問題有更深的理解了?這樣手動生成目錄太累,所以我們可以在編譯的時候這樣 javac -d . TestDosRunning.java 編譯的同時為我們生成的這樣的目錄結構。

    方式二

    既然解釋器會找這樣的目錄結構,我們何不利用現成的,即我們可以回退到com父目錄,在這里直接 java com.study.mengyi.access.TestDosRunning ,這時當前目錄為: D:\JavaLearning\ThinkInJava\src

    經檢驗確實可以這樣!

    總結

    在java中類的唯一性靠包名命名空間來得到保證,這樣 在運行某個類必須冠以全類名的形式并對應好相應的目錄結構 ,這樣程序才可以正常運行。java的域名方式表示包名和包名對應操作系統的文件目錄結構真是妙哉!美哉!

     

    來自:http://blog.csdn.net/dawn_after_dark/article/details/75303207

     

 本文由用戶 笨的舍 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!