当我启动SWT应用程序(通过Eclipse启动配置文件)时,我收到以下堆栈跟踪:
Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/jface/resource/FontRegistry
at org.eclipse.jface.resource.JFaceResources.getFontRegistry(JFaceResources.java:338)
at org.eclipse.jface.window.Window.close(Window.java:313)
at org.eclipse.jface.dialogs.Dialog.close(Dialog.java:971)
at org.eclipse.jface.dialogs.ProgressMonitorDialog.close(ProgressMonitorDialog.java:348)
at org.eclipse.jface.dialogs.ProgressMonitorDialog.finishedRun(ProgressMonitorDialog.java:582)
at org.eclipse.jface.dialogs.ProgressMonitorDialog.run(ProgressMonitorDialog.java:498)
at com.blah.si.workflow.SWTApplication.main(SWTApplication.java:135)
现在,令这个奇怪的是:
ClassNotFoundException
。该类位于类路径中。如果我将源附加到jar,我可以调试到getFontRegistry方法。该方法将成功执行多次,最后在第338行抛出NoClassDefFoundError
。第337行是“if variable == null”语句,检查静态变量是否已初始化。第338行正在初始化它,如果它尚未初始化。第一次通过时,空检查失败,并执行初始化。在随后的方法传递中,null检查通过,因此返回已初始化的静态值。在最后一次传递(失败的那个)上,null检查再次失败(即使静态变量已经初始化),当它尝试重新初始化静态变量时,抛出NoClassDefFoundError
。这是相关的源(从第336行开始,请注意,fontRegistry是一个私有的静态变量,在其他任何地方都没有设置):
public static FontRegistry getFontRegistry() {
if (fontRegistry == null) {
fontRegistry = new FontRegistry(
"org.eclipse.jface.resource.jfacefonts");
}
return fontRegistry;
}
由于上面#3中的特殊性,我怀疑某种奇怪的类加载器行为 - 似乎最后一次通过该方法是在另一个类加载器中?
想法?
更新:Pourquoi Litytestdata提供的答案促使我注意在ProgressMonitorDialog第458行上方的try块中发生的事情。实际上,该代码抛出了一个异常,它被finally块吞噬了。根本原因是另一个缺少类(缺少的类不是JFontRegistry或其任何直接相关的类,而是另一个在边缘情况下依赖于蜘蛛网的。)我正在推荐所有回答指向我注意类路径,并接受Pourquoi的,因为这是突破。谢谢大家。
答案 0 :(得分:3)
听起来你错过了一个持有依赖关系的JAR文件,正如2006年7月的博客文章中所提到的那样,由Sanjiv JIVAN撰写:
Difference between ClassNotFoundException and NoClassDefFoundError
ClassLoader找不到报告的类时,会抛出
ClassNotFoundException
。
这通常意味着CLASSPATH
中缺少班级 这也可能意味着有问题的类正在尝试从父类ClassLoader
中加载的另一个类加载,因此子ClassLoader
中的类不可见。
在像App Server这样的更复杂的环境中工作时有时会出现这种情况(WebSphere对于此类ClassLoader
问题很臭名昭着。)人们往往会将
java.lang.NoClassDefFoundError
与java.lang.ClassNotFoundException
混为一谈。然而,有一个重要的区别。例如,异常(错误,因为
java.lang.NoClassDefFoundError
是java.lang.Error
的子类),例如
java.lang.NoClassDefFoundError:
org/apache/activemq/ActiveMQConnectionFactory
并不意味着ActiveMQConnectionFactory类不在
事实上,它恰恰相反。CLASSPATH
中。这意味着
ActiveMQConnectionFactory
找到了类ClassLoader
但是在尝试加载类时,它会在读取类定义时遇到错误。当有问题的类具有静态块或使用
ClassLoader
未找到的类的成员时,通常会发生这种情况。因此,要查找罪魁祸首,请查看相关课程的来源(本例中为
ActiveMQConnectionFactory
),使用静态块或静态成员查找代码。
如果您无权访问源代码,则只需使用 JAD 对其进行反编译。在检查代码时,假设您找到如下所示的代码行,请确保
CLASSPATH
中的类SomeClass。
private static SomeClass foo = new SomeClass();
提示:要了解某个类属于哪个jar,您可以使用网站 jarFinder 。这允许您使用通配符指定类名,并在其数据库中搜索该类 jarhoo允许您执行相同的操作,但不再可以自由使用。
如果您想在本地路径中找到某个类属于哪个jar,您可以使用 jarscan 等实用程序。您只需指定要查找的类以及您希望它开始在jar和zip文件中搜索类的根目录路径。
答案 1 :(得分:2)
我认为上面介绍的堆栈跟踪隐藏了真正的问题。以下是方法run
中的代码
org.eclipse.jface.dialogs.ProgressMonitorDialog(我添加评论):
public void run(boolean fork, boolean cancelable,
IRunnableWithProgress runnable) throws InvocationTargetException,
InterruptedException {
setCancelable(cancelable);
try {
aboutToRun();
// Let the progress monitor know if they need to update in UI Thread
progressMonitor.forked = fork;
ModalContext.run(runnable, fork, getProgressMonitor(), getShell()
.getDisplay());
} finally {
finishedRun(); // this is line 498
}
}
Jared的堆栈跟踪中的第二个底线是此类的第498行,即finishedRun()
块中finally
的调用。我怀疑真正的原因是在try
块中抛出异常。由于finally
块中的代码也会抛出异常,因此原始异常将丢失。
答案 2 :(得分:2)
为了更好地处理它是否是类加载器问题,请查看它工作的代码并添加:
try
{
final Class clazz;
final ClassLoader loader;
clazz = Class.forName("org/eclipse/jface/resource/FontRegistry");
loader = clazz.getClassLoader();
System.out.println("The classloader at step 1 is: " + loader);
}
catch(final Throwable ex)
{
ex.printStackTrace();
}
然后执行相同的操作,获取NoClassDefFoundError并查看类加载器是否不同。
然后,您将能够确保它是不同的ClassLoader。你能报告一下这发生了什么吗?根据结果,我可能会有更多的想法。
答案 3 :(得分:1)
要添加到excellent TofuBeer's answer,因为NoClassDefFoundError
表示:
org.eclipse.jface.resource.FontRegistry
ClassLoader
,
Class
找不到的ClassLoader
的成员。 让我们看一下org.eclipse.jface.resource.FontRegistry
source code:
它没有任何静态变量初始化(也没有超类)。
让我们看一下 org.eclipse.jface.resource.JFaceResources
source code
触发错误的getFontRegistry()
函数使用静态变量fontRegistry :
/**
* The JFace font registry; <code>null</code> until lazily initialized or
* explicitly set.
*/
private static FontRegistry fontRegistry = null;
因此, begs 引发 question:为什么static initialized variable会突然被视为null
?
因为不知怎的FontRegistry
or JFaceResources
get unloaded by the gc?!
如果一个字段被声明为static,那么无论该类最终可以创建多少个实例(可能为零),都只存在该字段的一个化身。静态字段(有时称为类变量)在初始化类时实现(第12.4节)。
因此,无论该类的实例是否随时存在都无关紧要,只要已经加载了Class本身,该字段就会存在。
如果这是一个 eclipse插件,这可能与此 FAQ entry
有关以下是新用户的典型情况:
您正在编写一个扩展插件XYZ的插件 要使其编译,可以从Java Build Path属性页或通过编辑.classpath文件向项目的构建路径添加对插件XYZ的JAR文件的引用。
启动运行时工作台时,会报告以下令人惊讶的错误:java.lang.NoClassDefFoundError: XYZ.SomeClass
。不要开始查看运行时工作台的启动配置中的Plug-ins and Fragments选项卡 该选项卡仅影响用于运行时工作台的插件,以及它们是从工作空间还是从Eclipse安装目录加载的。
相反,请开始查看插件清单 编辑plugin.xml文件并确保将XYZ视为必需的插件 然后,保存plugin.xml文件 这将自动更新项目的构建路径。
在编写插件时,切勿手动编辑.classpath文件 插件清单编辑器只会覆盖您对其所做的任何更改。不是很文明,但这就是它的工作方式。
答案 4 :(得分:1)
如果您尝试自己加载FontRegistry类(如TofoBeer所述),您会发现如果使用FontRegistry,以下JAR的类是依赖类。
org.eclipse.core.commands_xxxxx.jar
您必须将此JAR添加到构建路径。