以前一直沒有注意過這個問題,前兩天機緣巧合上網查了一下,然后自己測試驗證了一下。雖然網上說法很多,但是關於NoClassDefFoundError並沒有給出一個樣例,所以一直無法理解,索性自己驗證了一下,收獲還不少。
ClassNotFoundException
ClassNotFoundException這個錯誤,比較常見也好理解。
原因:就是找不到指定的class。
常見的場景就是:
1 調用class的forName方法時,找不到指定的類
2 ClassLoader 中的 findSystemClass() 方法時,找不到指定的類
3 ClassLoader 中的 loadClass() 方法時,找不到指定的類
開發者平時會有這樣一種使用方法,類似JDBC加載驅動!
1 package test321; 2 3 public class test { 4 public static void main(String[] args) { 5 try { 6 Class.forName("test321.hello"); 7 } catch (ClassNotFoundException e) { 8 e.printStackTrace(); 9 } 10 } 11 }
此時,程序會到當前的目錄中尋找指定位置test321.hello這個class。
並且這個類也是可以正常執行的。
但是,我們修改一下加載的類名,這樣顯然是找不到指定的類的。
1 package test321; 2 3 public class test { 4 public static void main(String[] args) { 5 try { 6 Class.forName("test321.hello1"); 7 } catch (ClassNotFoundException e) { 8 e.printStackTrace(); 9 } 10 } 11 }
此時就會報錯!
報錯! java.lang.ClassNotFoundException: test321.hello1 at java.net.URLClassLoader$1.run(URLClassLoader.java:366) 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 java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:190) at test321.test.main(test.java:6)
原因就是找不到指定的string對應的class文件。
NoClassDefFoundError
這個就比較奇葩了,查找其他的資料是說,通過了編譯,但是使用的時候,比如new的時候會出錯。
通過查找資料,搜集到如下的場景:
1 類依賴的class或者jar不存在
2 類文件存在,但是存在不同的域中
3 大小寫問題,javac編譯的時候是無視大小的,很有可能你編譯出來的class文件就與想要的不一樣!這個沒有做驗證。
針對上面的第二點,做了個關於包名的驗證:
另一種情況就是由於你通過了編譯,但是這個類是有包名的,因此在編譯時需要指定classpath,在使用的時候需要加上包名才可以。
下面做了一個小例子!
在沒有包名的情況下,我們看一下正常情況是什么樣子的。
1 public class test { 2 public static void main(String[] args) { 3 System.out.println("test"); 4 } 5 }
而如果這個類中包含包名,那么按照上面的方法編譯,使用時就會報錯!
1 package ccc; 2 3 public class test { 4 public static void main(String[] args) { 5 System.out.println("test"); 6 } 7 }
很明顯,報錯信息中指出了包的信息!那么怎么辦呢?
在編譯時,加上【 -d . 】這樣可以把當前的目錄加入到classpath中。
在使用時,加上包名就可以了!
也就是說,這個含有包名的類,編譯時,需要指定classpath的路徑,並且使用的時候指定包名全路徑,才可以。
參考資料
1 http://blog.csdn.net/magister_feng/article/details/7459151
2 http://www.blogjava.net/leekiang/archive/2007/04/26/113810.html
3 http://blog.sina.com.cn/s/blog_65c507190100hzs0.html