Java.lang.IncompatibleClassChangeError'a ne sebep olur?


218

Bir Java kütüphanesini JAR olarak paketliyorum ve ondan java.lang.IncompatibleClassChangeErroryöntemleri çağırmaya çalıştığımda birçok s atıyor . Bu hatalar rastgele görünüyor. Bu hataya ne tür sorunlar neden olabilir?


Apache FOP 1.0 ve Barcode4J'yi test ettiğim bir Eclipse projesinde, Barcode4J ile gelen ek kütüphaneler görünüşe göre FOP ile gelenleri geçersiz kılıyorlardı (bazıları daha yüksek sürüm numaralarına sahipti). Yapım yolunuza / sınıf yolunuza ne koyduğunuza çok dikkat etmek için bir durum.
Wivani

Yanıtlar:


170

Bu, istemci kodunu yeniden derlemeden kitaplıkta bazı uyumsuz ikili değişiklikler yaptığınız anlamına gelir. Java Dil Belirtimi §13 , bu tür tüm değişiklikleri, en belirgin şekilde, staticözel olmayan alanları / yöntemleri değiştirmeyi staticveya tersini değiştirmeyi ayrıntılarıyla açıklar .

İstemci kodunu yeni kitaplığa karşı yeniden derlediğinizde işe koyulmanız iyi olur.

GÜNCELLEME: Bir halk kütüphanesi yayınlıyorsanız, "ikili geriye dönük uyumluluk" olarak bilinenleri korumak için mümkün olduğunca uyumsuz ikili değişiklikler yapmaktan kaçınmalısınız. Tek başına bağımlılık kavanozlarını güncellemek, uygulamayı veya yapıyı bozmamalıdır. İkili geriye dönük uyumluluğu kırmanız gerekiyorsa, değişikliği yayınlamadan önce ana sürüm numarasının (ör. 1.xy'den 2.0.0'a) artırılması önerilir .


2
Bazı nedenlerden dolayı, buradaki geliştiriciler, istemci kodunun yeniden derlenmesinin sorunu tam olarak çözmediği bir sorun yaşıyorlar. Bazı nedenlerden dolayı, dosyayı bulunduğu yerde düzenler ve hatayı yeniden derlerse, artık kitaplıkta başvurulan projenin başka bir yerinde rastgele açılır. Bunun ne olabileceğini merak ediyorum.
Zombies

5
Temiz bir yapı (tüm *.classdosyaları sil ) ve yeniden derleme yapmaya çalıştınız mı ? Dosya düzenleme benzer etkiye sahiptir.
notnoop

Dinamik olarak oluşturulan kod yok .... bir JSP böyle düşünmüyorsanız. Sınıf dosyalarını silmeyi denedik ve bu yardımcı olmadı. Garip olan şey, bunun benim için görünmüyor olması, ancak diğer geliştiriciler için olması.
Zombies

2
Temiz bir yapı yaptığınızda, koştuğunuz kavanozlara karşı derlediğinizden emin olabilir misiniz?
notnoop

32bit veya 64bit platformuyla ilgili bir şey var mı, sadece 64bit platformunda bir hata buldum.
cowboi-peng

96

Yeni paketlenmiş kitaplığınız eski sürümle geriye dönük ikili uyumlu (BC) değildir . Bu nedenle, yeniden derlenmeyen bazı kitaplık istemcileri özel durum atabilir.

Bu, kitaplığın eski bir sürümüyle oluşturulan istemcilerin java.lang atmasına neden olabilecek Java kitaplığı API'sindeki değişikliklerin tam bir listesidir. IncompatibleClassChangeError yeni bir tane üzerinde çalışırlarsa (yani BC'yi kırmak):

  1. Nihai olmayan alan statik hale gelir,
  2. Sabit olmayan alan statik değildir,
  3. Sınıf arayüz olur,
  4. Arayüz sınıf haline gelir,
  5. sınıfa / arabirime yeni bir alan eklerseniz (veya yeni süper sınıf / süper arabirim eklerseniz), istemci sınıfı C'nin süper arabirimindeki statik alan, alandan devralınan eklenmiş bir alanı (aynı adla) gizleyebilir C'nin süper sınıfı (çok nadir bir durum).

Not : Birçok vardır diğer istisnalar : diğer uyumsuz değişikliklerin neden NoSuchFieldError , NoSuchMethodError , IllegalAccessError , InstantiationError , VerifyError , NoClassDefFoundError ve AbstractMethodError .

BC hakkında daha iyi bir makale Jim des Rivières tarafından yazılan "Java tabanlı API'leri Geliştirme 2: API İkili Uyumluluğunu Sağlama" dır .

Bu değişiklikleri tespit etmek için bazı otomatik araçlar da vardır :

Kütüphaneniz için japi-uygunluk denetleyicisinin kullanımı:

japi-compliance-checker OLD.jar NEW.jar

Clirr aracının kullanımı:

java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar

İyi şanslar!


59

Bu yanıtların hepsi doğru olmakla birlikte, sorunun çözümü genellikle daha zordur. Genellikle, sınıfyoluna aynı bağımlılığın iki farklı hafif versiyonunun sonucudur ve neredeyse her zaman ya başlangıçta sınıf yolunda olmaktan ya da geçişli kapamanın bazı ithalatı farklı olmaktan farklı olarak, ancak genellikle sınıfta olmaktan farklı bir üst sınıftan kaynaklanır. örnekleme ve kurucu çağırma. (Başarılı sınıf yükleme ve ctor çağırma işleminden sonra, alırsınız NoSuchMethodExceptionya da almazsınız .)

Davranış rasgele görünüyorsa, büyük olasılıkla ilk önce hangi kodun vurulduğuna bağlı olarak farklı geçiş bağımlılıkları yükleyen çok iş parçacıklı bir program sınıfının sonucudur.

Bunları çözmek için VM'yi -verbosebağımsız değişken olarak başlatmayı deneyin , ardından kural dışı durum oluştuğunda yüklenen sınıflara bakın. Bazı şaşırtıcı bilgiler görmelisiniz. Örneğin, aynı bağımlılığa sahip birden fazla kopyaya ve bunların dahil edildiğini bildiğinizde hiç beklemediğiniz veya kabul etmediğiniz sürümlere sahip olması.

Maven ile yinelenen kavanozların çözülmesi en iyi şekilde Maven (veya SBT'nin Bağımlılık Grafiği Eklentisi) altındaki maven bağımlılığı eklentisi ve maven-uygulayıcı eklentisinin bir kombinasyonu ile yapılır , daha sonra bu kavanozları üst düzey POM'nizin bir bölümüne veya içe aktarılan bağımlılık olarak ekleyin SBT'deki öğeler (bu bağımlılıkları kaldırmak için).

İyi şanslar!


5
Ayrıntılı argüman hangi kavanozların sorun çıkardığını belirlememe yardımcı oldu. Teşekkürler
Virat Kadaru

6

Ayrıca, JNI kullanırken, C ++ 'dan bir Java yöntemi çağrıldığında, yanlış sırada çağrılan Java yöntemine parametreleri iletirseniz, (çünkü onlar çağrılan yöntem içindeki parametreleri kullanmaya çalıştığınızda bu hata alırsınız) keşfettim doğru tür olmayacak). Başlangıçta JNI yöntemini çağırdığınızda sınıf imzası kontrolünün bir parçası olarak sizin için bu kontrolü yapmaz, ama polimorfik parametreleri geçiriyor olabilirsiniz ve onlar yapmak zorunda olduğunu varsayıyorum ne yaptığınızı bildiğinizi varsayalım.

Örnek C ++ JNI Kodu:

void invokeFooDoSomething() {
    jobject javaFred = FredFactory::getFred(); // Get a Fred jobject
    jobject javaFoo = FooFactory::getFoo(); // Get a Foo jobject
    jobject javaBar = FooFactory::getBar(); // Get a Bar jobject
    jmethodID methodID = getDoSomethingMethodId() // Get the JNI Method ID


    jniEnv->CallVoidMethod(javaFoo,
                           methodID,
                           javaFred, // Woops!  I switched the Fred and Bar parameters!
                           javaBar);

    // << Insert error handling code here to discover the JNI Exception >>
    //  ... This is where the IncompatibleClassChangeError will show up.
}

Örnek Java Kodu:

class Bar { ... }

class Fred {
    public int size() { ... }
} 

class Foo {
    public void doSomething(Fred aFred, Bar anotherObject) {
        if (name.size() > 0) { // Will throw a cryptic java.lang.IncompatibleClassChangeError
            // Do some stuff...
        }
    }
}

1
İpucu için teşekkürler. Yanlış örnek (bu) sağlanan bir Java yöntemi çağrıldığında aynı sorunu vardı.
sstn

5

Aynı sorunu yaşadım ve daha sonra uygulama sürüm 6'da derlenirken uygulamayı Java sürüm 1.4 üzerinde çalıştırdığımı anladım.

Aslında, yinelenen bir kitaplığa sahip olmanın nedeni, biri sınıfyolunun içinde, diğeri sınıf yolunda bulunan bir kavanoz dosyasının içine dahil edilmişti.


Bunun altına nasıl geldiniz? Im benzer bir sorun var eminim ama nasıl hangi bağımlılık kütüphanesi çoğaltılı izleme için bir ipucu yok.
beterthanlife

@beterthanlife, yinelenen sınıfları arayan tüm jar dosyalarının içinde arama yapan bir komut dosyası yazın (tam adıyla, yani paket adıyla arama) :)
Eng.Fouad

Tamam, NetBeans ve maven-shade kullanarak Ben yinelenen tüm sınıfları ve içinde bulundukları jar dosyalarını görebilirsiniz düşünüyorum. Bu jar dosyaları çoğu benim doğrudan pom.xml başvuruda bulunmadım, bu yüzden onlar gerekir varsayalım bağımlılıklarıma dahil edilmek. Bağımlılıkların her bir pom dosyasından geçmeden hangi bağımlılığın dahil edildiğini bulmanın kolay bir yolu var mı? (lütfen noobishness benim affet, ben bir java bakire!)
beterthanlife

@beterthanlife Bununla ilgili yeni bir soru sorun :)
Eng.Fouad

Sınıf bağımlılıkları grafiğine (./gradlew: <ad>: bağımlılıklar) bakarak yinelenen bir kavanoz buldum. Bu yüzden, lib'in eski versiyonunu projeye çeken suçluyu buldum.
nyx


1

Glassfish ile savaşı çözerken ve yeniden dağıtırken bu sorunla karşılaştım. Sınıf yapım şöyleydi,

public interface A{
}

public class AImpl implements A{
}

ve olarak değiştirildi

public abstract class A{
}

public class AImpl extends A{
}

Etki alanını durdurup yeniden başlattıktan sonra iyi çalıştı. Glassfish 3.1.43 kullanıyordum


1

Yerel makinemin tomcat'ında (8.0.20) mükemmel şekilde dağıtılan bir web uygulamam var. Ancak, qa ortamına koyduğumda (tomcat - 8.0.20), bana IncompatibleClassChangeError vermeyi sürdürdü ve bir arabirime genişlettiğimden şikayet ediyordu. Bu arayüz soyut bir sınıfa dönüştürüldü. Ve ebeveyn ve çocuk sınıflarını derledim ve hala aynı sorunu almaya devam ettim. Son olarak, hata ayıklamak istedim, bu yüzden üst sürümdeki sürümü x.0.1-SNAPSHOT olarak değiştirdim ve sonra her şeyi derledim ve şimdi çalışıyor. Burada verilen yanıtları izledikten sonra birisi hala sorunu çözüyorsa, lütfen pom.xml dosyasındaki sürümlerin de doğru olduğundan emin olun. Bunun işe yarayıp yaramadığını görmek için sürümleri değiştirin. Öyleyse, sürüm sorununu düzeltin.


1

Cevabımın Intellij'e özgü olacağına inanıyorum.

Ben bile "out" ve "target" dirs silmek gibi gidiyor temiz, yeniden inşa vardı. Intellij, bazen garip hataları silen bir "önbellekleri geçersiz kıl ve yeniden başlat" özelliğine sahiptir. Bu sefer işe yaramadı. Bağımlılık sürümlerinin tümü proje ayarları-> modüller menüsünde doğru görünüyordu.

Son cevap, yerel bağımlılık depomdan sorun bağımlılığımı el ile silmekti. Bouncycastle'ın eski bir versiyonu suçluydu (sadece sürümleri değiştirdiğimi biliyordum ve bu sorun olurdu) ve eski sürüm inşa edilen yerde hiçbir şey göstermese de sorunumu çözdü. Intellij 14 sürümünü kullanıyordum ve bu işlem sırasında 15'e yükselttim.


1

Benim durumumda, bu hatayla bu şekilde karşılaştım. pom.xmlProjemin iki bağımlılıkları tanımlanır Ave B. Ve her ikisi de Ave Baynı eser (diyoruz üzerinde tanımlı bağımlılık C) ama (farklı sürümleri C.1ve C.2). Bu olduğunda, Cmaven'deki her sınıf için iki sürümden sınıfın yalnızca bir sürümünü seçebilir ( uber-jar oluştururken ). Bağımlılık uyumlulaştırma kurallarına göre "en yakın" sürümü seçer ve " Yinelenen bir sınıfımız var ..." uyarısı verir. Bir yöntem / sınıf imzası sürümler arasında değişirse java.lang.IncompatibleClassChangeError, yanlış sürüm çalışma zamanında kullanılır.

Gelişmiş: Eğer Abir v1 kullanmalıdır Cve Bbir v2 kullanmalıdır Co zaman gerekir, taşınmaya C içinde Ave Bhem bağlıdır nihai projeyi oluştururken önlemek sınıf çatışması 'ın ponlar (biz yinelenen bir sınıf uyarı var) Ave B.


1

Benim durumumda, com.nimbusdsdağıtılan uygulamamdaki kitaplığı eklediğimde hata ortaya çıktı Websphere 8.5.
Aşağıdaki istisna oluştu:

Nedeni: java.lang.IncompatibleClassChangeError: org.objectweb.asm.AnnotationVisitor

Çözüm asm kavanozunu kütüphaneden hariç tutmaktı:

<dependency>
    <groupId>com.nimbusds</groupId>
    <artifactId>nimbus-jose-jwt</artifactId>
    <version>5.1</version>
    <exclusions>
        <exclusion>
            <artifactId>asm</artifactId>
            <groupId>org.ow2.asm</groupId>
        </exclusion>
    </exclusions>
</dependency>

0

Lütfen kodunuzun aynı sınıf adlarına ve paket tanımına sahip iki modül projesinden oluşup oluşmadığını kontrol edin. Örneğin, birisi önceki uygulamaya dayalı yeni arabirim uygulaması oluşturmak için kopyala-yapıştır kullanırsa bu olabilir.


0

Bu, bu hatanın olası oluşumlarının bir kaydıysa:

Ben sadece bir BeanInstantiationException CXF ExtensionException bir IncompatibleClassChangeError yuvarlandı bahar (3.1.1_release) yapılandırma CXF (2.6.0) yükleme sırasında WAS (8.5.0.1), bu hatayı aldım. Aşağıdaki snippet, yığın izlemesinin özünü gösterir:

Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.apache.cxf.bus.spring.SpringBus]: Constructor threw exception; nested exception is org.apache.cxf.bus.extension.ExtensionException
            at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:162)
            at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:76)
            at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:990)
            ... 116 more
Caused by: org.apache.cxf.bus.extension.ExtensionException
            at org.apache.cxf.bus.extension.Extension.tryClass(Extension.java:167)
            at org.apache.cxf.bus.extension.Extension.getClassObject(Extension.java:179)
            at org.apache.cxf.bus.extension.ExtensionManagerImpl.activateAllByType(ExtensionManagerImpl.java:138)
            at org.apache.cxf.bus.extension.ExtensionManagerBus.<init>(ExtensionManagerBus.java:131)
            [etc...]
            at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147)
            ... 118 more

Caused by: java.lang.IncompatibleClassChangeError: 
org.apache.neethi.AssertionBuilderFactory
            at java.lang.ClassLoader.defineClassImpl(Native Method)
            at java.lang.ClassLoader.defineClass(ClassLoader.java:284)
            [etc...]
            at com.ibm.ws.classloader.CompoundClassLoader.loadClass(CompoundClassLoader.java:586)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:658)
            at org.apache.cxf.bus.extension.Extension.tryClass(Extension.java:163)
            ... 128 more

Bu durumda çözüm, savaş dosyamdaki modülün sınıf yolu sırasını değiştirmekti. Yani, aşağıdaki WAS konsolunda savaş uygulamasını açın ve istemci modüllerini seçin. Modül yapılandırmasında, sınıf yüklemesini "son ana öğe" olarak ayarlayın.

WAS konsolunda bulunur:

  • Uygulama -> Uygulama Türleri -> WebSphere Enterprise Uygulamaları
  • Başvurunuzu temsil eden bağlantıyı tıklayın (savaş)
  • "Modüller" bölümünün altındaki "Modülleri Yönet" i tıklayın
  • Temel modül (ler) için bağlantıya tıklayın
  • "Sınıf yükleyici siparişi" ni "(son ana öğe)" olarak değiştirin.

0

Çok fazla zaman harcadıktan sonra başka bir senaryoyu belgelemek.

Üzerinde EJB ek açıklaması olan bir sınıfa sahip bir bağımlılık kavanozunuz olmadığından emin olun.

@localEk açıklama içeren ortak bir kavanoz dosyamız vardı . Bu sınıf daha sonra bu ortak projeden ve ana ejb kavanoz projemize taşındı. Bizim ejb kavanozumuz ve ortak kavanozumuz bir kulak içinde paketlenmiştir. Ortak kavanoz bağımlılığımızın sürümü güncellenmedi. Böylece 2 sınıf uyumsuz değişikliklerle bir şey olmaya çalışıyor.


0

Yukarıdakilerin hepsi - herhangi bir sebepten dolayı büyük bir refactor yapıyordum ve bunu almaya başladım. Arayüzümün bulunduğu paketi yeniden adlandırdım ve temizledim. Umarım yardımcı olur.


0

Bazı nedenlerden dolayı, JNI kullanılırken ve a çağrılırken jobject yerine jclass argümanını iletirken aynı istisna atılır Call*Method().

Bu Ogre Psalm33'ün cevabına benzer.

void example(JNIEnv *env, jobject inJavaList) {
    jclass class_List = env->FindClass("java/util/List");

    jmethodID method_size = env->GetMethodID(class_List, "size", "()I");
    long size = env->CallIntMethod(class_List, method_size); // should be passing 'inJavaList' instead of 'class_List'

    std::cout << "LIST SIZE " << size << std::endl;
}

Sorulduktan 5 yıl sonra bu soruya cevap vermek için biraz geç olduğunu biliyorum, ancak bu arama yaparken en iyi hitlerden biri bu java.lang.IncompatibleClassChangeErroryüzden bu özel vakayı belgelemek istedim.


0

2 sentimi ekliyorum. Scala ve sbt ve scala-logging'i bağımlılık olarak kullanıyorsanız; bu durum scala-logging'in önceki sürümünün scala-logging-api.So ismine sahip olması nedeniyle olabilir; scala uygulamasını başlatırken çalışma zamanı hatalarına yol açan adlar.


0

Bu sorunun ek bir nedeni, Instant RunAndroid Studio için etkinleştirmiş olmanızdır .

Çözüm

Bu hatayı almaya başladığınızı fark ederseniz kapatın Instant Run.

  1. Android Studio ana ayarları
  2. Oluşturma, Yürütme, Dağıtım
  3. Anında Çalıştırma
  4. "Anında çalıştırmayı etkinleştir ..." seçeneğinin işaretini kaldırın

Neden

Instant Runçalışan Uygulamanıza güncelleme sağlamak için daha hızlı hale getirmek amacıyla geliştirme sırasında çok sayıda şeyi değiştirir. Böylece anında çalışma. Çalıştığında, gerçekten yararlıdır. Ancak, bunun gibi bir sorun ortaya çıktığında, yapılacak en iyi şey Instant RunAndroid Studio'nun bir sonraki sürümüne kadar kapatmaktır .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.