在平常Java开发中,咱们常常碰到java.lang.NoClassDefFoundError这样的错误,须要花费不少时间去找错误的缘由,具体是哪一个类不见了?类明明还在,为何找不到?并且咱们很容易把java.lang.NoClassDefFoundError和java.lang.ClassNotfoundException这两个错误搞混,事实上这两个错误是彻底不一样的。咱们每每花费时间去不断尝试一些其余的方法去解决这个问题,而没有真正去理解这个错误的缘由。这篇文章就是经过解决NoClassDefFoundError错误处理的经验分享来揭开NoClassDefFoundError的一些秘密。NoClassDefFoundError的错误并不是不能解决或者说很难解决,只是这种错误的表现形式很容易迷惑其余的Java开发者。下面咱们来分析下为何会发生NoClassDefFoundError这样的错误,以及怎样去解决这个错误。html
NoClassDefFoundError错误的发生,是由于Java虚拟机在编译时能找到合适的类,而在运行时不能找到合适的类致使的错误。例如在运行时咱们想调用某个类的方法或者访问这个类的静态成员的时候,发现这个类不可用,此时Java虚拟机就会抛出NoClassDefFoundError错误。与ClassNotFoundException的不一样在于,这个错误发生只在运行时须要加载对应的类不成功,而不是编译时发生。不少Java开发者很容易在这里把这两个错误搞混。java
简单总结就是,NoClassDefFoundError发生在编译时对应的类可用,而运行时在Java的classpath路径中,对应的类不可用致使的错误。发生NoClassDefFoundError错误时,你能看到以下的错误日志:linux
Exception in thread "main" java.lang.NoClassDefFoundError
错误的信息很明显地指明main线程没法找到指定的类,而这个main线程可能时主线程或者其余子线程。若是是主线程发生错误,程序将崩溃或中止,而若是是子线程,则子线程中止,其余线程继续运行。框架
咱们常常被java.lang.ClassNotFoundException和java.lang.NoClassDefFoundError这两个错误迷惑不清,尽管他们都与Java classpath有关,可是他们彻底不一样。NoClassDefFoundError发生在JVM在动态运行时,根据你提供的类名,在classpath中找到对应的类进行加载,但当它找不到这个类时,就发生了java.lang.NoClassDefFoundError的错误,而ClassNotFoundException是在编译的时候在classpath中找不到对应的类而发生的错误。ClassNotFoundException比NoClassDefFoundError容易解决,是由于在编译时咱们就知道错误发生,而且彻底是因为环境的问题致使。而若是你在J2EE的环境下工做,而且获得NoClassDefFoundError的异常,并且对应的错误的类是确实存在的,这说明这个类对于类加载器来讲,多是不可见的。this
根据前文,很明显NoClassDefFoundError的错误是由于在运行时类加载器在classpath下找不到须要加载的类,因此咱们须要把对应的类加载到classpath中,或者检查为何类在classpath中是不可用的,这个发生可能的缘由以下:spa
下面咱们看一些当发生NoClassDefFoundError时,咱们该如何解决的样例。操作系统
/** * Java program to demonstrate how failure of static initialization subsequently cause * java.lang.NoClassDefFoundError in Java. * @author Javin Paul */ public class NoClassDefFoundErrorDueToStaticInitFailure { public static void main(String args[]){ List<User> users = new ArrayList<User>(2); for(int i=0; i<2; i++){ try{ users.add(new User(String.valueOf(i))); //will throw NoClassDefFoundError }catch(Throwable t){ t.printStackTrace(); } } } } class User{ private static String USER_ID = getUserId(); public User(String id){ this.USER_ID = id; } private static String getUserId() { throw new RuntimeException("UserId Not found"); } } Output java.lang.ExceptionInInitializerError at testing.NoClassDefFoundErrorDueToStaticInitFailure.main(NoClassDefFoundErrorDueToStaticInitFailure.java:23) Caused by: java.lang.RuntimeException: UserId Not found at testing.User.getUserId(NoClassDefFoundErrorDueToStaticInitFailure.java:41) at testing.User.<clinit>(NoClassDefFoundErrorDueToStaticInitFailure.java:35) ... 1 more java.lang.NoClassDefFoundError: Could not initialize class testing.User at testing.NoClassDefFoundErrorDueToStaticInitFailure.main(NoClassDefFoundErrorDueToStaticInitFailure.java:23) Read more: http://javarevisited.blogspot.com/2011/06/noclassdeffounderror-exception-in.html#ixzz3dqtbvHDy
在有多个ClassLoader的J2EE的环境中,很容易出现NoClassDefFoundError的错误。因为J2EE没有指明标准的类加载器,使用的类加载器依赖与不一样的容器像Tomcat、WebLogic,WebSphere加载J2EE的不一样组件如War包或者EJB-JAR包。关于类加载器的相关知识能够参考这篇文章类加载器的工做原理。.net
总结来讲,类加载器基于三个机制:委托、可见性和单一性,委托机制是指将加载一个类的请求交给父类加载器,若是这个父类加载器不可以找到或者加载这个类,那么再加载它。可见性的原理是子类的加载器能够看见全部的父类加载器加载的类,而父类加载器看不到子类加载器加载的类。单一性原理是指仅加载一个类一次,这是由委托机制确保子类加载器不会再次加载父类加载器加载过的类。如今假设一个User类在WAR文件和EJB-JAR文件都存在,而且被WAR ClassLoader加载,而WAR ClassLoader是加载EJB-JAR ClassLoader的子ClassLoader。当EJB-JAR中代码引用这个User类时,加载EJB-JAR全部class的Classloader找不到这个类,由于这个类已经被EJB-JAR classloader的子加载器WAR classloader加载。线程
这会致使的结果就是对User类出现NoClassDefFoundError异常,而若是在两个JAR包中这个User类都存在,若是你使用equals方法比较两个类的对象时,会出现ClassCastException的异常,由于两个不一样类加载器加载的类没法进行比较。3d
有时候会出现Exception in thread “main” java.lang.NoClassDefFoundError: com/sun/tools/javac/Main 这样的错误,这个错误说明你的Classpath, PATH 或者 JAVA_HOME没有安装配置正确或者JDK的安装不正确。这个问题的解决办法时从新安装你的JDK。
java.lang.NoClassDefFoundError: testing/User at testing.NoClassDefFoundErrorDueToStaticInitFailure.main(NoClassDefFoundErrorDueToStaticInitFailure.java:23)
如今咱们知道要怎样去面对NoClassDefFoundError异常并解决它了。
参考文章:
3 ways to solve java.lang.NoClassDefFoundError in Java J2EE