2023年11月29日发(作者:)
解决NoClassDefFound(转载)
前⾔
在⽇常Java开发中,我们经常碰到sDefFoundError这样的错误,需要花费很多时间去找错误的原因,具体是哪个类
不见了?类明明还在,为什么找不到?⽽且我们很容易把sDefFoundError和otfoundException这两
个错误搞混,事实上这两个错误是完全不同的。我们往往花费时间去不断尝试⼀些其他的⽅法去解决这个问题,⽽没有真正去理解这个错误
的原因。这篇⽂章就是通过解决NoClassDefFoundError错误处理的经验分享来揭开NoClassDefFoundError的⼀些秘密。
NoClassDefFoundError的错误并⾮不能解决或者说很难解决,只是这种错误的表现形式很容易迷惑其他的Java开发者。下⾯我们来分析
下为什么会发⽣NoClassDefFoundError这样的错误,以及怎样去解决这个错误。
NoClassDefFoundError错误发⽣的原因
NoClassDefFoundError错误的发⽣,是因为Java虚拟机在编译时能找到合适的类,⽽在运⾏时不能找到合适的类导致的错误。例如在
运⾏时我们想调⽤某个类的⽅法或者访问这个类的静态成员的时候,发现这个类不可⽤,此时Java虚拟机就会抛出
NoClassDefFoundError错误。与ClassNotFoundException的不同在于,这个错误发⽣只在运⾏时需要加载对应的类不成功,⽽不是编
译时发⽣。很多Java开发者很容易在这⾥把这两个错误搞混。
简单总结就是,NoClassDefFoundError发⽣在编译时对应的类可⽤,⽽运⾏时在Java的classpath路径中,对应的类不可⽤导致的错
误。发⽣NoClassDefFoundError错误时,你能看到如下的错误⽇志:
Exception in thread "main" java.lang.NoClassDefFoundError
错误的信息很明显地指明main线程⽆法找到指定的类,⽽这个main线程可能时主线程或者其他⼦线程。如果是主线程发⽣错误,程序将
运⾏时明确指定你认为程序能正常运⾏的 -classpath 参数,如果增加之后程序能正常运⾏,说明原来程序的classpath被其他⼈覆盖了。
NoClassDefFoundError也可能由于类的静态初始化模块错误导致,当你的类执⾏⼀些静态初始化模块操作,如果初始化模块抛出异常,
哪些依赖这个类的其他类会抛出NoClassDefFoundError的错误。如果你查看程序⽇志,会发现⼀些
ionInInitializerError的错误⽇志,ExceptionInInitializerError的错误会导致sDefFoundError: Could
not initialize class,如下⾯的代码⽰例:
/**
* Java program to demonstrate how failure of static initialization subsequently cause
* sDefFoundError 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
由于NoClassDefFoundError是LinkageError的⼦类,⽽LinkageError的错误在依赖其他的类时会发⽣,所以如果你的程序依赖原⽣
的类库和需要的dll不存在时,有可能出现sDefFoundError。这种错误也可能抛出sfiedLinkError:
no dll in Exception Java这样的异常。解决的办法是把依赖的类库和dll跟你的jar包放在⼀起。
如果你使⽤Ant构建脚本来⽣成jar⽂件和manifest⽂件,要确保Ant脚本获取的是正确的classpath值写⼊到⽂件
Jar⽂件的权限问题也可能导致NoClassDefFoundError,如果你的程序运⾏在像linux这样多⽤户的操作系统种,你需要把你应⽤相
关的资源⽂件,如Jar⽂件,类库⽂件,配置⽂件的权限单独分配给程序所属⽤户组,如果你使⽤了多个⽤户不同程序共享的jar包时,
很容易出现权限问题。⽐如其他⽤户应⽤所属权限的jar包你的程序没有权限访问,会导致sDefFoundError的错误。
基于XML配置的程序也可能导致NoClassDefFoundError的错误。⽐如⼤多数Java的框架像Spring,Struts使⽤xml配置获取对应的
bean信息,如果你输⼊了错误的名称,程序可能会加载其他错误的类⽽导致NoClassDefFoundError异常。我们在使⽤Spring
MVC框架或者Apache Struts框架,在部署War⽂件或者EAR⽂件时就经常会出现Exception in thread
“main” sDefFoundError。
在有多个ClassLoader的J2EE的环境中,很容易出现NoClassDefFoundError的错误。由于J2EE没有指明标准的类加载器,使⽤的
类加载器依赖与不同的容器像Tomcat、WebLogic,WebSphere加载J2EE的不同组件如War包或者EJB-JAR包。关于类加载器的相
关知识可以参考这篇⽂章类加载器的⼯作原理。
总结来说,类加载器基于三个机制:委托、可见性和单⼀性,委托机制是指将加载⼀个类的请求交给⽗类加载器,如果这个⽗类加载器不
能够找到或者加载这个类,那么再加载它。可见性的原理是⼦类的加载器可以看见所有的⽗类加载器加载的类,⽽⽗类加载器看不到⼦类加
载器加载的类。单⼀性原理是指仅加载⼀个类⼀次,这是由委托机制确保⼦类加载器不会再次加载⽗类加载器加载过的类。现在假设⼀个
User类在WAR⽂件和EJB-JAR⽂件都存在,并且被WAR ClassLoader加载,⽽WAR ClassLoader是加载EJB-JAR ClassLoader的⼦
ClassLoader。当EJB-JAR中代码引⽤这个User类时,加载EJB-JAR所有class的Classloader找不到这个类,因为这个类已经被EJB-JAR


发布评论