سؤال

أنا أحصل على NoClassDefFoundError عندما أقوم بتشغيل تطبيق Java الخاص بي.ما هو عادة سبب هذا؟

هل كانت مفيدة؟

المحلول

يحدث هذا عندما يكون هناك ملف فئة يعتمد عليه الكود الخاص بك ويكون موجودًا في وقت الترجمة ولكن لم يتم العثور عليه في وقت التشغيل.ابحث عن الاختلافات في وقت البناء ومسارات وقت التشغيل.

نصائح أخرى

في حين أنه من الممكن أن يكون هذا بسبب عدم تطابق مسار الفئة بين وقت الترجمة ووقت التشغيل، إلا أن هذا ليس صحيحًا بالضرورة.

من المهم أن نضع استثناءين أو ثلاثة استثناءات مختلفة في أذهاننا في هذه الحالة:

  1. java.lang.ClassNotFoundException يشير هذا الاستثناء إلى أنه لم يتم العثور على الفئة في مسار الفئة.يشير هذا إلى أننا كنا نحاول تحميل تعريف الفئة، ولم يكن الفصل موجودًا في مسار الفصل.

  2. java.lang.NoClassDefFoundError يشير هذا الاستثناء إلى أن JVM بحث في بنية بيانات تعريف الفئة الداخلية الخاصة به عن تعريف الفئة ولم يعثر عليها.وهذا يختلف عن القول بأنه لا يمكن تحميله من مسار الفصل.يشير هذا عادةً إلى أننا حاولنا سابقًا تحميل فصل من مسار الفصل، لكننا فشلنا لسبب ما - نحاول الآن استخدام الفصل مرة أخرى (وبالتالي نحتاج إلى تحميله، لأنه فشل في المرة السابقة)، لكننا' لن نحاول حتى تحميله، لأننا فشلنا في تحميله سابقًا (ونشك بشكل معقول في أننا سنفشل مرة أخرى).قد يكون الفشل السابق عبارة عن ClassNotFoundException أو ExceptionInInitializerError (يشير إلى فشل في كتلة التهيئة الثابتة) أو أي عدد من المشكلات الأخرى.النقطة المهمة هي أن NoClassDefFoundError ليس بالضرورة مشكلة في مسار الفئة.

هنا هو الرمز للتوضيح java.lang.NoClassDefFoundError.لطفا أنظر إجابة جاريد للحصول على شرح مفصل.

NoClassDefFoundErrorDemo.java

public class NoClassDefFoundErrorDemo {
    public static void main(String[] args) {
        try {
            // The following line would throw ExceptionInInitializerError
            SimpleCalculator calculator1 = new SimpleCalculator();
        } catch (Throwable t) {
            System.out.println(t);
        }
        // The following line would cause NoClassDefFoundError
        SimpleCalculator calculator2 = new SimpleCalculator();
    }

}

SimpleCalculator.java

public class SimpleCalculator {
    static int undefined = 1 / 0;
}

لقد وجدت أنه في بعض الأحيان أحصل على خطأ NoClassDefFound عندما يتم تجميع التعليمات البرمجية باستخدام إصدار غير متوافق من الفئة الموجودة في وقت التشغيل.المثيل المحدد الذي أتذكره هو مع مكتبة Apache axis.كان هناك بالفعل إصداران في مسار الفصل الدراسي الخاص بي في وقت التشغيل وكان يلتقط الإصدار القديم وغير المتوافق وليس الإصدار الصحيح، مما تسبب في حدوث خطأ NoClassDefFound.كان هذا في تطبيق سطر الأوامر حيث كنت أستخدم أمرًا مشابهًا لهذا.

set classpath=%classpath%;axis.jar

لقد تمكنت من الحصول عليه لالتقاط الإصدار المناسب باستخدام:

set classpath=axis.jar;%classpath%;

NoClassDefFoundError في جافا

تعريف:

  1. يتعذر على Java Virtual Machine العثور على فئة معينة في وقت التشغيل والتي كانت متاحة في وقت الترجمة.

  2. إذا كان الفصل موجودًا أثناء وقت الترجمة ولكنه غير متاح في Java classpath أثناء وقت التشغيل.

enter image description here

أمثلة:

  1. الفصل ليس موجودًا في Classpath، ولا توجد طريقة مؤكدة للتعرف عليه ولكن في كثير من الأحيان يمكنك فقط إلقاء نظرة على طباعة System.getproperty("java.classpath") وسوف يطبع مسار الفصل من هناك يمكنك على الأقل الحصول عليه فكرة عن مسار الفصل الدراسي الفعلي الخاص بك.
  2. مثال بسيط على NoClassDefFoundError هو أن الفئة تنتمي إلى ملف JAR مفقود أو لم تتم إضافة JAR إلى مسار الفصل أو في بعض الأحيان تم تغيير اسم الجرة من قبل شخص ما كما في حالتي قام أحد زملائي بتغيير tibco.jar إلى tibco_v3.jar والبرنامج هو الفشل مع java.lang.NoClassDefFoundError وكنت أتساءل ما هو الخطأ.

  3. ما عليك سوى محاولة التشغيل باستخدام خيار -classpath بشكل صريح مع مسار الفصل الذي تعتقد أنه سيعمل، وإذا كان يعمل، فهذه علامة قصيرة مؤكدة على أن شخصًا ما يتجاوز مسار فئة جافا.

  4. يمكن أن تتسبب مشكلة الإذن في ملف JAR أيضًا في حدوث NoClassDefFoundError في Java.
  5. يمكن أن يتسبب الخطأ المطبعي في تكوين XML أيضًا في حدوث NoClassDefFoundError في Java.
  6. عندما لا تكون فئتك المترجمة التي تم تعريفها في الحزمة موجودة في نفس الحزمة أثناء التحميل كما في حالة JApplet، فإنها سترمي NoClassDefFoundError في Java.

الحلول الممكنة:

  1. الفصل غير متوفر في Java Classpath.
  2. إذا كنت تعمل في بيئة J2EE، فإن رؤية الفئة بين Classloader المتعددة يمكن أن تسبب أيضًا java.lang.NoClassDefFoundError، راجع قسم الأمثلة والسيناريو للمناقشة التفصيلية.
  3. تحقق من وجود java.lang.ExceptionInInitializerError في ملف السجل الخاص بك.يعد NoClassDefFoundError بسبب فشل التهيئة الثابتة أمرًا شائعًا جدًا.
  4. نظرًا لأن NoClassDefFoundError هو فئة فرعية من java.lang.LinkageError، فقد يأتي أيضًا إذا لم تكن إحدى تبعياته مثل المكتبة الأصلية متاحة.
  5. أي برنامج نصي لبدء التشغيل يتجاوز متغير بيئة Classpath.
  6. ربما تقوم بتشغيل برنامجك باستخدام أمر jar ولم يتم تعريف الفئة في سمة ClassPath الخاصة بملف البيان.

موارد:

3 طرق لحل مشكلة NoClassDefFoundError

java.lang.NoClassDefFoundError أنماط المشكلة

هذا ال أفضل حل لقد وجدت حتى الآن.

لنفترض أن لدينا حزمة تسمى org.mypackage تحتوي على الطبقات:

  • HelloWorld (الطبقة الرئيسية)
  • فئة الدعم
  • UtilClass

ويتم تخزين الملفات التي تحدد هذه الحزمة فعليًا ضمن الدليل D:\myprogram (على نظام التشغيل Windows) أو /home/user/myprogram (على لينكس).

سيبدو هيكل الملف كما يلي:enter image description here

عندما نقوم باستدعاء Java، نحدد اسم التطبيق المراد تشغيله: org.mypackage.HelloWorld.ومع ذلك، يجب علينا أيضًا إخبار Java بمكان البحث عن الملفات والأدلة التي تحدد الحزمة الخاصة بنا.لذلك، لتشغيل البرنامج، علينا استخدام الأمر التالي:enter image description here

كنت أستخدم إطار الربيع مع مخضرم وحل هذا الخطأ في مشروعي.

حدث خطأ أثناء التشغيل في الفصل الدراسي.كنت أقرأ خاصية كعدد صحيح، ولكن عندما قرأت القيمة من ملف الخاصية، كانت قيمتها مضاعفة.

لم يمنحني Spring تتبعًا كاملاً للخط الذي فشل وقت التشغيل فيه.قال ببساطة NoClassDefFoundError.ولكن عندما قمت بتنفيذه كتطبيق Java أصلي (بإخراجه من MVC)، فقد نجح ExceptionInInitializerError وهو السبب الحقيقي وكيف تتبعت الخطأ.

أعطتني إجابة @ xli نظرة ثاقبة على ما قد يكون خطأ في الكود الخاص بي.

أحصل على NoClassFoundError عندما لا تتمكن الفئات التي تم تحميلها بواسطة أداة تحميل فئة وقت التشغيل من الوصول إلى الفئات التي تم تحميلها بالفعل بواسطة أداة تحميل جذر Java.نظرًا لأن أدوات تحميل الفئات المختلفة موجودة في مجالات أمان مختلفة (وفقًا لـ java)، فلن يسمح jvm بحل الفئات التي تم تحميلها بالفعل بواسطة أداة تحميل الجذر في مساحة عنوان أداة تحميل وقت التشغيل.

قم بتشغيل برنامجك باستخدام "java -javaagent:tracer.jar [YOUR java ARGS]"

وينتج مخرجات توضح الفئة المحملة وبرنامج التحميل الذي قام بتحميل الفئة.من المفيد جدًا تتبع سبب عدم إمكانية حل الفصل.

// ClassLoaderTracer.java
// From: https://blogs.oracle.com/sundararajan/entry/tracing_class_loading_1_5

import java.lang.instrument.*;
import java.security.*;

// manifest.mf
// Premain-Class: ClassLoadTracer

// jar -cvfm tracer.jar manifest.mf ClassLoaderTracer.class

// java -javaagent:tracer.jar  [...]

public class ClassLoadTracer 
{
    public static void premain(String agentArgs, Instrumentation inst) 
    {
        final java.io.PrintStream out = System.out;
        inst.addTransformer(new ClassFileTransformer() {
            public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

                String pd = (null == protectionDomain) ? "null" : protectionDomain.getCodeSource().toString();
                out.println(className + " loaded by " + loader + " at " + new java.util.Date() + " in " + pd);

                // dump stack trace of the thread loading class 
                Thread.dumpStack();

                // we just want the original .class bytes to be loaded!
                // we are not instrumenting it...
                return null;
            }
        });
    }
}

اقرأ هذا خاصة إذا كنت ترى NoClassDefFoundErrors في اختبارات الوحدة...


إحدى الحالات المثيرة للاهتمام التي قد ترى فيها الكثير NoClassDefFoundErrors هو عندما:

  1. throw أ RuntimeException في ال static كتلة من صفك Example
  2. اعترضها (أو إذا لم يكن الأمر مهمًا كما لو تم إلقاؤها في ملف حالة اختبار)
  3. حاول إنشاء مثيل لهذه الفئة Example

static class Example {
    static {
        thisThrowsRuntimeException();
    }
}

static class OuterClazz {

    OuterClazz() {
        try {
            new Example();
        } catch (Throwable ignored) { //simulating catching RuntimeException from static block
            // DO NOT DO THIS IN PRODUCTION CODE, THIS IS JUST AN EXAMPLE in StackOverflow
        }

        new Example(); //this throws NoClassDefFoundError
    }
}

NoClassDefError سيتم طرحها برفقة ExceptionInInitializerError من الكتلة الساكنة RuntimeException.


هذه حالة مهمة بشكل خاص عندما ترى NoClassDefFoundErrors في الخاص بك اختبارات الوحدة.

بطريقة ما أنت "تشارك" الـ static منع التنفيذ بين الاختبارات، ولكن الأولي ExceptionInInitializerError سيكون فقط في حالة اختبار واحدة.أول من استخدم الإشكالية Example فصل.حالات الاختبار الأخرى التي تستخدم Example سوف الطبقة رمي فقط NoClassDefFoundErrors.

في حالة إنشاء كود (EMF، وما إلى ذلك) فقد يكون هناك عدد كبير جدًا من أدوات التهيئة الثابتة التي تستهلك مساحة المكدس بالكامل.

راجع سؤال Stack Overflow كيفية زيادة حجم مكدس جافا؟.

ساعدتني التقنية أدناه عدة مرات:

System.out.println(TheNoDefFoundClass.class.getProtectionDomain().getCodeSource().getLocation());

حيث TheNoDefFoundClass هي الفئة التي قد تكون "مفقودة" بسبب تفضيل إصدار أقدم من نفس المكتبة التي يستخدمها برنامجك.يحدث هذا غالبًا في الحالات التي يتم فيها نشر برنامج العميل في حاوية مهيمنة، مسلحًا ببرامج تحميل الفئات الخاصة به والعديد من الإصدارات القديمة من libs الأكثر شيوعًا.

لقد أصلحت مشكلتي عن طريق تعطيل preDexLibraries لجميع الوحدات:

dexOptions {
        preDexLibraries false
        ...

NoClassDefFoundError يمكن أن يحدث أيضًا عندما أ ثابتة يحاول المُهيئ تحميل حزمة موارد غير متوفرة في وقت التشغيل، على سبيل المثال، ملف خصائص تحاول الفئة المتأثرة تحميله من META-INF الدليل، ولكن ليس هناك.إذا لم تقبض NoClassDefFoundError, في بعض الأحيان لن تتمكن من رؤية تتبع المكدس بالكامل؛للتغلب على هذا يمكنك استخدام مؤقتا catch شرط ل Throwable:

try {
    // Statement(s) that cause the affected class to be loaded
} catch (Throwable t) {
    Logger.getLogger("<logger-name>").info("Loading my class went wrong", t);
}

إذا جاء شخص ما إلى هنا بسبب java.lang.NoClassDefFoundError: org/apache/log4j/Logger خطأ، في حالتي تم إنتاجه لأنني استخدمت log4j 2 (لكنني لم أقم بإضافة جميع الملفات التي تأتي معه)، واستخدمت بعض مكتبات التبعية log4j 1.كان الحل هو إضافة جسر Log4j 1.x:المرطبان log4j-1.2-api-<version>.jar والذي يأتي مع log4j 2.مزيد من المعلومات في log4j 2 الهجرة.

نسختين مختلفتين من نفس المشروع

في حالتي، كانت المشكلة هي عدم قدرة Eclipse على التمييز بين نسختين مختلفتين من نفس المشروع.لدي واحد مقفل على الجذع (التحكم في إصدار SVN) والآخر يعمل في فرع واحد في كل مرة.لقد جربت تغييرًا واحدًا في نسخة العمل كحالة اختبار JUnit، والتي تضمنت استخراج فئة داخلية خاصة لتكون فئة عامة بمفردها وأثناء عملها، قمت بفتح النسخة الأخرى من المشروع لإلقاء نظرة على بعض العناصر الأخرى جزء من الكود الذي يحتاج إلى تغييرات.في مرحلة ما، NoClassDefFoundError برزت تشكو من عدم وجود الطبقة الداخلية الخاصة؛أدى النقر المزدوج على تتبع المكدس إلى نقلي إلى الملف المصدر في نسخة المشروع الخاطئة.

أدى إغلاق النسخة الرئيسية للمشروع وتشغيل حالة الاختبار مرة أخرى إلى التخلص من المشكلة.

يمكن أن يكون سبب هذا الخطأ غير محدد نسخة جافا متطلبات.

في حالتي، تمكنت من حل هذا الخطأ، أثناء إنشاء مشروع مفتوح المصدر رفيع المستوى، عن طريق التبديل من Java 9 إلى Java 8 باستخدام SDKMAN!.

sdk list java
sdk install java 8u152-zulu
sdk use java 8u152-zulu

ثم قم بإجراء تثبيت نظيف كما هو موضح أدناه.


عند الاستخدام مخضرم باعتبارها أداة البناء الخاصة بك، فمن المفيد في بعض الأحيان - وعادةً ما يكون ذلك أمرًا ممتعًا - أن تقوم بذلك ينظف بناء "تثبيت". مع تعطيل الاختبار.

mvn clean install -DskipTests

الآن ذلك كل شئ تم بناؤه وتثبيته، يمكنك المضي قدمًا وإجراء الاختبارات.

mvn test

لقد حصلت على أخطاء NoClassDefFound عندما لم أقم بتصدير فئة في علامة التبويب "الطلب والتصدير" في مسار Java Build الخاص بمشروعي.تأكد من وضع علامة اختيار في علامة التبويب "الطلب والتصدير" لأي تبعيات تضيفها إلى مسار إنشاء المشروع.يرى تحذير من الخسوف:لن يتم تصدير أو نشر XXXXXXXXX.jar.قد ينتج عن ذلك استثناءات ClassNotFoundExceptions الخاصة بوقت التشغيل.

لم تتمكن Java من العثور على الفئة A في وقت التشغيل.كانت الفئة A في مشروع ArtClient المخضرم من مساحة عمل مختلفة.لذلك قمت باستيراد ArtClient إلى مشروع Eclipse الخاص بي.كان اثنان من مشاريعي يستخدمان ArtClient كتبعية.لقد قمت بتغيير مرجع المكتبة إلى مرجع المشروع لهؤلاء (مسار البناء -> تكوين مسار البناء).

وذهبت المشكلة.

لقد واجهت نفس المشكلة، وكنت في المخزون لعدة ساعات.

لقد وجدت الحل.في حالتي، كانت هناك طريقة ثابتة محددة بسبب ذلك.لا يمكن لـ JVM إنشاء كائن آخر من تلك الفئة.

على سبيل المثال،

private static HttpHost proxy = new HttpHost(proxyHost, Integer.valueOf(proxyPort), "http");

وصلتني هذه الرسالة بعد إزالة ملفين من مكتبة SRC، وعندما قمت بإعادتهما ظلت أرى رسالة الخطأ هذه.

الحل الخاص بي كان:أعد تشغيل الكسوف.ومنذ ذلك الحين لم أر هذه الرسالة مرة أخرى :-)

تأكد من تطابق هذا في module:app و module:lib:

android {
    compileSdkVersion 23
    buildToolsVersion '22.0.1'
    packagingOptions {
    }

    defaultConfig {
        minSdkVersion 17
        targetSdkVersion 23
        versionCode 11
        versionName "2.1"
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top