java.lang.classnotfounderror:com/sun/tools/javac/main

轉自:http://bbs.itheima.com/thread-45487-1-1.html

最近一直使用eclipse沒有使用文本編譯器。然後呢,javac命令出錯。
C:\Users\lyk>javac
Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/tools/javac/M
ain
Caused by: java.lang.ClassNotFoundException: com.sun.tools.javac.Main
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
Could not find the main class: com.sun.tools.javac.Main.  Program will exit.

如圖:


原因:

可能是因爲導包問題導致的。
建議重新配置一下環境變量
配置環境變量:
java_home: C:\……\jdk1.6.0
path:      C:\……\jdk1.6.0\bin
classpath: C:\……\jdk1.6.0\lib ; .
-------------------------
補充一句,貌似eclipse的jvm是自帶的,所以在eclipse中可以運行。還有,去看一下你環境變量配置下的lib文件夾在不在。


轉自:http://www.blogjava.net/pandawang/archive/2008/01/31/64639.html

首先讓我們來分析一下java源文件的編譯,運行吧!javac命令是調用“JAVA_HOME/lib/tools.jar”中的“com.sun.tools.javac.Main”的compile方法來編譯:

   public static int compile(String as[]);

   public static int compile(String as[], PrintWriter printwriter);

返回0表示編譯成功,字符串數組as則是我們用javac命令編譯時的參數,以空格劃分。例如:
javac -classpath c:\foo\bar.jar;. -d c:\ c:\Some.java
則字符串數組as爲{"-classpath","c:\\foo\\bar.jar;.","-d","c:\\","c:\\Some.java"},如果帶有PrintWriter參數,則會把編譯信息出到這個指定的printWriter中。默認的輸出是System.err。

其中 Main是由JVM使用Launcher初始化的system classloader載入的,根據全盤負責原則,編譯器在解析這個java源文件時所發現的它所依賴和引用的所有Class也將由system classloader載入,如果system classloader不能載入某個Class時,編譯器將拋出一個“cannot resolve symbol”錯誤。

所以首先編譯就通不過,也就是編譯器無法編譯一個引用了不在CLASSPATH中的未知Class的java源文件,而由於拼寫錯誤或者沒有把所需類庫放到CLASSPATH中,大家一定經常看到這個“cannot resolve symbol”這個編譯錯誤吧!

其次,就是我們把這個Class放到編譯路徑中,成功的進行了編譯,然後在運行的時候不把它放入到CLASSPATH中而利用我們自己的 classloader來動態載入這個Class,這時候也會出現“java.lang.NoClassDefFoundError”的違例,爲什麼呢?

我們再來分析一下,首先調用這個造型語句的可執行的Class一定是由JVM使用Launcher初始化的system classloader載入的,根據全盤負責原則,當我們進行造型的時候,JVM也會使用system classloader來嘗試載入這個Class來對實例進行造型,自然在system classloader尋找不到這個Class時就會拋出“java.lang.NoClassDefFoundError”的違例。

OK,現在讓我們來總結一下,java文件的編譯和Class的載入執行,都是使用Launcher初始化的system classloader作爲類載入器的,我們無法動態的改變system classloader,更無法讓JVM使用我們自己的classloader來替換system classloader,根據全盤負責原則,就限制了編譯和運行時,我們無法直接顯式的使用一個system classloader尋找不到的Class,即我們只能使用Java核心類庫,擴展類庫和CLASSPATH中的類庫中的Class。

還不死心!再嘗試一下這種情況,我們把這個Class也放入到CLASSPATH中,讓system classloader能夠識別和載入。然後我們通過自己的classloader來從指定的class文件中載入這個Class(不能夠委託 parent載入,因爲這樣會被system classloader從CLASSPATH中將其載入),然後實例化一個Object,並造型成這個Class,這樣JVM也識別這個Class(因爲 system classloader能夠定位和載入這個Class從CLASSPATH中),載入的也不是CLASSPATH中的這個Class,而是從 CLASSPATH外動態載入的,這樣總行了吧!十分不幸的是,這時會出現“java.lang.ClassCastException”違例。

爲什麼呢?我們也來分析一下,不錯,我們雖然從CLASSPATH外使用我們自己的classloader動態載入了這個Class,但將它的實例造型的時候是JVM會使用system classloader來再次載入這個Class,並嘗試將使用我們的自己的classloader載入的Class的一個實例造型爲system classloader載入的這個Class(另外的一個)。大家發現什麼問題了嗎?也就是我們嘗試將從一個classloader載入的Class的一個實例造型爲另外一個classloader載入的Class,雖然這兩個Class的名字一樣,甚至是從同一個class文件中載入。但不幸的是JVM 卻認爲這個兩個Class是不同的,即JVM認爲不同的classloader載入的相同的名字的Class(即使是從同一個class文件中載入的)是不同的!這樣做的原因我想大概也是主要出於安全性考慮,這樣就保證所有的核心Java類都是system classloader載入的,我們無法用自己的classloader載入的相同名字的Class的實例來替換它們的實例。

看到這裏,聰明的讀者一定想到了該如何動態載入我們的Class,實例化,造型並調用了吧!

那就是利用面向對象的基本特性之一的多形性。我們把我們動態載入的Class的實例造型成它的一個system classloader所能識別的父類就行了!這是爲什麼呢?我們還是要再來分析一次。當我們用我們自己的classloader來動態載入這我們只要把這個Class的時候,發現它有一個父類Class,在載入它之前JVM先會載入這個父類Class,這個父類Class是system classloader所能識別的,根據委託機制,它將由system classloader載入,然後我們的classloader再載入這個Class,創建一個實例,造型爲這個父類Class,注意了,造型成這個父類 Class的時候(也就是上溯)是面向對象的java語言所允許的並且JVM也支持的,JVM就使用system classloader再次載入這個父類Class,然後將此實例造型爲這個父類Class。大家可以從這個過程發現這個父類Class都是由 system classloader載入的,也就是同一個class loader載入的同一個Class,所以造型的時候不會出現任何異常。而根據多形性,調用這個父類的方法時,真正執行的是這個Class(非父類 Class)的覆蓋了父類方法的方法。這些方法中也可以引用system classloader不能識別的Class,因爲根據全盤負責原則,只要載入這個Class的classloader即我們自己定義的 classloader能夠定位和載入這些Class就行了。

這樣我們就可以事先定義好一組接口或者基類並放入CLASSPATH中,然後在執行的時候動態的載入實現或者繼承了這些接口或基類的子類。還不明白嗎?讓我們來想一想Servlet吧,web application server能夠載入任何繼承了Servlet的Class並正確的執行它們,不管它實際的Class是什麼,就是都把它們實例化成爲一個Servlet Class,然後執行Servlet的init,doPost,doGet和destroy等方法的,而不管這個Servlet是從web- inf/lib和web-inf/classes下由system classloader的子classloader(即定製的classloader)動態載入。說了這麼多希望大家都明白了。在applet,ejb等容器中,都是採用了這種機制.

對於以上各種情況,希望大家實際編寫一些example來實驗一下。

最後我再說點別的, classloader雖然稱爲類加載器,但並不意味着只能用來加載Class,我們還可以利用它也獲得圖片,音頻文件等資源的URL,當然,這些資源必須在CLASSPATH中的jar類庫中或目錄下。我們來看API的doc中關於ClassLoader的兩個尋找資源和Class的方法描述吧:
        public URL getResource(String name)
        用指定的名字來查找資源,一個資源是一些能夠被class代碼訪問的在某種程度上依賴於代碼位置的數據(圖片,音頻,文本等等)。
               一個資源的名字是以'/'號分隔確定資源的路徑名的。
               這個方法將先請求parent classloader搜索資源,如果沒有parent,則會在內置在虛擬機中的classloader(即bootstrap classloader)的路徑中搜索。如果失敗,這個方法將調用findResource(String)來尋找資源。
        public static URL getSystemResource(String name)
               從用來載入類的搜索路徑中查找一個指定名字的資源。這個方法使用system class loader來定位資源。即相當於ClassLoader.getSystemClassLoader().getResource(name)。

例如:
   System.out.println(ClassLoader.getSystemResource("java/lang/String.class"));
的結果爲:
   jar:文件:/C:/j2sdk1.4.1_01/jre/lib/rt.jar!/java/lang/String.class
表明String.class文件在rt.jar的java/lang目錄中。
因此我們可以將圖片等資源隨同Class一同打包到jar類庫中(當然,也可單獨打包這些資源)並添加它們到class loader的搜索路徑中,我們就可以無需關心這些資源的具體位置,讓class loader來幫我們尋找了!

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