Điều gì gây ra java.lang.IncompiverseClassChangeError?


218

Tôi đang đóng gói một thư viện Java dưới dạng JAR và nó sẽ ném nhiều java.lang.IncompatibleClassChangeErrors khi tôi cố gắng gọi các phương thức từ nó. Những lỗi này dường như xuất hiện ngẫu nhiên. Những loại vấn đề có thể gây ra lỗi này?


Trong một dự án Eclipse nơi tôi đang thử nghiệm Apache FOP 1.0 và Barcode4J, các thư viện bổ sung đi kèm với Barcode4J dường như đã ghi đè lên các thư viện đi kèm với FOP (một số có số phiên bản cao hơn). Đó là một trường hợp rất cẩn thận với những gì bạn đặt trong đường dẫn xây dựng / đường dẫn lớp.
Wivani

Câu trả lời:


170

Điều này có nghĩa là bạn đã thực hiện một số thay đổi nhị phân không tương thích với thư viện mà không biên dịch lại mã máy khách. Đặc tả ngôn ngữ Java §13 chi tiết tất cả các thay đổi như vậy, nổi bật nhất, thay đổi các statictrường / phương thức không riêng tư thành statichoặc ngược lại.

Biên dịch lại mã máy khách đối với thư viện mới và bạn nên truy cập.

CẬP NHẬT: Nếu bạn xuất bản một thư viện công cộng, bạn nên tránh thực hiện các thay đổi nhị phân không tương thích càng nhiều càng tốt để duy trì cái gọi là "tương thích ngược nhị phân". Cập nhật các lọ phụ thuộc một mình lý tưởng không nên phá vỡ ứng dụng hoặc bản dựng. Nếu bạn phải phá vỡ tính tương thích ngược nhị phân, bạn nên tăng số phiên bản chính (ví dụ: từ 1.xy lên 2.0.0) trước khi phát hành thay đổi.


2
Vì một số lý do, các nhà phát triển ở đây đang gặp vấn đề trong đó việc biên dịch lại mã máy khách không khắc phục được vấn đề một cách chính xác. Vì một số lý do nếu họ chỉnh sửa tệp nơi xảy ra và biên dịch lại lỗi không còn xảy ra ở đó, nhưng nhiều hơn sẽ xuất hiện ngẫu nhiên ở nơi khác trong dự án, nơi thư viện được tham chiếu. Tôi tò mò không biết điều gì có thể xảy ra.
Zombie

5
Bạn đã thử thực hiện một bản dựng sạch (xóa tất cả các *.classtệp) và biên dịch lại chưa? Chỉnh sửa tập tin có hiệu lực tương tự.
không có

Không có mã được tạo động .... trừ khi bạn sẽ coi một JSP là như vậy. Chúng tôi đã thử xóa các tệp lớp và điều này dường như không có ích. Điều tồi tệ hơn là nó dường như không xảy ra với tôi nhưng nó xảy ra với các nhà phát triển khác.
Zombie

2
Bạn có thể đảm bảo rằng khi bạn thực hiện một bản dựng sạch, bạn đang biên dịch dựa trên cùng các tệp bạn chạy cùng không?
không có

Có điều gì đó liên quan đến nền tảng 32 bit hoặc 64 bit, tôi thấy một lỗi chỉ thực hiện trong nền tảng 64 bit.
cowboi-peng

96

Thư viện mới được đóng gói của bạn không tương thích nhị phân ngược (BC) với phiên bản cũ. Vì lý do này, một số ứng dụng khách thư viện không được biên dịch lại có thể đưa ra ngoại lệ.

Đây là danh sách đầy đủ các thay đổi trong API thư viện Java có thể khiến các máy khách được xây dựng với phiên bản cũ của thư viện ném java.lang. Không tương thíchClassChangeError nếu chúng chạy trên một cái mới (tức là phá BC):

  1. Trường không cuối cùng trở thành tĩnh,
  2. Trường không cố định trở thành không tĩnh,
  3. Lớp trở thành giao diện,
  4. Giao diện trở thành lớp,
  5. nếu bạn thêm một trường mới vào lớp / giao diện (hoặc thêm siêu lớp / siêu giao diện mới) thì một trường tĩnh từ siêu giao diện của lớp khách C có thể ẩn một trường đã thêm (có cùng tên) được kế thừa từ siêu hạng C (trường hợp rất hiếm).

Lưu ý : Có nhiều trường hợp ngoại lệ khác gây ra bởi các thay đổi không tương thích khác: NoSuchFieldError , NoSuchMethodError , IllegalAccessError , InstantiationError , ConfirmError , NoClassDefFoundErrorAbstractMethodError .

Bài viết hay hơn về BC là "Phát triển API dựa trên Java 2: Đạt được tính tương thích nhị phân API" được viết bởi Jim des Rivières.

Ngoài ra còn có một số công cụ tự động để phát hiện những thay đổi như vậy:

Sử dụng trình kiểm tra tuân thủ japi cho thư viện của bạn:

japi-compliance-checker OLD.jar NEW.jar

Sử dụng công cụ clirr:

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

Chúc may mắn!


59

Trong khi những câu trả lời này đều đúng, việc giải quyết vấn đề thường khó khăn hơn. Nhìn chung, đó là kết quả của hai phiên bản khác nhau nhẹ của cùng một phụ thuộc vào đường dẫn lớp và hầu như luôn được gây ra bởi một siêu lớp khác so với ban đầu được tạo ra so với trên đường dẫn lớp hoặc một số lần nhập của đóng cửa bắc cầu là khác nhau, nhưng nói chung là ở lớp khởi tạo và gọi hàm tạo. (Sau khi tải lớp thành công và gọi ctor, bạn sẽ nhận được NoSuchMethodExceptionhoặc không có gì.)

Nếu hành vi xuất hiện ngẫu nhiên, có khả năng đó là kết quả của một lớp chương trình đa luồng tải các phụ thuộc bắc cầu khác nhau dựa trên mã nào bị tấn công trước.

Để giải quyết những điều này, hãy thử khởi chạy VM với -verbosetư cách là một đối số, sau đó xem xét các lớp đang được tải khi ngoại lệ xảy ra. Bạn sẽ thấy một số thông tin đáng ngạc nhiên. Chẳng hạn, có nhiều bản sao của cùng một phụ thuộc và các phiên bản mà bạn không bao giờ mong đợi hoặc sẽ chấp nhận nếu bạn biết chúng được đưa vào.

Giải quyết các lọ trùng lặp với Maven được thực hiện tốt nhất với sự kết hợp giữa plugin-phụ thuộc mavenplugin -thực thi-maven trong Maven (hoặc Plugin Biểu đồ phụ thuộc của SBT , sau đó thêm các lọ đó vào một phần của POM cấp cao nhất của bạn hoặc dưới dạng phụ thuộc được nhập các yếu tố trong SBT (để loại bỏ các phụ thuộc đó).

Chúc may mắn!


5
Đối số dài dòng đã giúp tôi xác định chính xác lọ nào đang gây ra vấn đề. Cảm ơn
Virat Kadaru

6

Tôi cũng đã phát hiện ra rằng, khi sử dụng JNI, gọi một phương thức Java từ C ++, nếu bạn truyền tham số cho phương thức Java được gọi theo thứ tự sai, bạn sẽ gặp lỗi này khi bạn cố sử dụng các tham số bên trong phương thức được gọi (vì chúng sẽ không phải là loại đúng). Ban đầu tôi rất ngạc nhiên rằng JNI không thực hiện việc kiểm tra này cho bạn như là một phần của kiểm tra chữ ký lớp khi bạn gọi phương thức, nhưng tôi cho rằng họ không thực hiện loại kiểm tra này vì bạn có thể truyền các tham số đa hình và họ phải giả sử bạn biết những gì bạn đang làm.

Ví dụ Mã JNI C ++:

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.
}

Mã Java ví dụ:

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
Cảm ơn đã gợi ý. Tôi gặp vấn đề tương tự khi gọi một phương thức Java với trường hợp sai (điều này) được cung cấp.
sstn

5

Tôi đã có cùng một vấn đề và sau đó tôi phát hiện ra rằng tôi đang chạy ứng dụng trên phiên bản Java 1.4 trong khi ứng dụng được biên dịch trên phiên bản 6.

Trên thực tế, lý do là có một thư viện trùng lặp, một thư viện nằm trong đường dẫn lớp và một thư viện khác được bao gồm trong một tệp jar nằm trong đường dẫn lớp.


Làm thế nào bạn đi đến tận cùng của điều này? Tôi chắc chắn tôi có một vấn đề tương tự nhưng không biết làm thế nào để theo dõi thư viện phụ thuộc nào đang bị trùng lặp.
beterthanlife

@beterthanlife viết một tập lệnh tìm kiếm bên trong tất cả các tệp jar tìm kiếm các lớp trùng lặp (tìm kiếm theo tên đủ điều kiện của họ, tức là với tên gói) :)
Eng.Fouad

ok, sử dụng NetBeans và maven-bóng râm Tôi nghĩ rằng tôi có thể thấy tất cả các lớp đang được sao chép và các tệp jar mà chúng cư trú. Nhiều tệp jar này tôi không tham chiếu trực tiếp trong pom.xml của mình, vì vậy tôi cho rằng chúng phải được bao gồm bởi các phụ thuộc của tôi. Có cách nào dễ dàng để tìm ra sự phụ thuộc nào bao gồm chúng, mà không cần thông qua từng tệp pom của phụ thuộc không? (xin hãy tha thứ cho tôi, tôi là một trinh nữ java!)
beterthanlife

@beterthanlife Tốt hơn nên hỏi một câu hỏi mới liên quan đến điều đó :)
Eng.Fouad

Tôi tìm thấy một bình trùng lặp bằng cách nhìn vào biểu đồ phụ thuộc lớp (./gradlew: <name>: phụ thuộc). Vì vậy, tôi đã tìm ra thủ phạm đã thu hút phiên bản cũ của lib vào dự án.
nyx

1

Một tình huống khác mà lỗi này có thể xuất hiện là với Bảo hiểm Mã Emma.

Điều này xảy ra khi gán Object cho giao diện. Tôi đoán điều này có liên quan đến việc Object được kết nối và không tương thích nhị phân nữa.

http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351

May mắn là sự cố này không xảy ra với Cobertura, vì vậy tôi đã thêm cobertura-maven-plugin trong các plugin báo cáo về pom.xml của mình


1

Tôi đã phải đối mặt với vấn đề này trong khi không triển khai và triển khai lại một cuộc chiến với cá thủy tinh. Cấu trúc lớp học của tôi là như thế này,

public interface A{
}

public class AImpl implements A{
}

và nó đã được đổi thành

public abstract class A{
}

public class AImpl extends A{
}

Sau khi dừng và khởi động lại tên miền, nó hoạt động tốt. Tôi đã sử dụng cá thủy tinh 3.1.43


1

Tôi có một ứng dụng web triển khai hoàn toàn tốt trên tomcat của máy cục bộ của tôi (8.0.20). Tuy nhiên, khi tôi đưa nó vào môi trường qa (tomcat - 8.0.20), nó vẫn tiếp tục đưa cho tôi IncompiverseClassChangeError và tôi đã phàn nàn rằng tôi đang mở rộng trên một giao diện. Giao diện này đã được thay đổi thành một lớp trừu tượng. Và tôi đã biên soạn các lớp cha mẹ và con và tôi vẫn tiếp tục gặp vấn đề tương tự. Cuối cùng, tôi muốn gỡ lỗi, vì vậy, tôi đã thay đổi phiên bản trên cha thành x.0.1-SNAPSHOT và sau đó biên dịch mọi thứ và bây giờ nó đang hoạt động. Nếu ai đó vẫn gặp phải vấn đề sau khi làm theo các câu trả lời được đưa ra ở đây, vui lòng đảm bảo rằng các phiên bản trong pom.xml của bạn cũng đúng. Thay đổi các phiên bản để xem nếu nó hoạt động. Nếu vậy, sau đó sửa vấn đề phiên bản.


1

Câu trả lời của tôi, tôi tin rằng, sẽ là Intellij cụ thể.

Tôi đã xây dựng lại sạch sẽ, thậm chí còn đi xa đến mức xóa thủ công các thư mục "ra" và "mục tiêu". Intellij có "bộ nhớ cache và khởi động lại không hợp lệ", đôi khi xóa các lỗi lẻ. Lần này nó không hoạt động. Tất cả các phiên bản phụ thuộc đều trông chính xác trong menu cài đặt dự án-> mô-đun.

Câu trả lời cuối cùng là xóa thủ công vấn đề của tôi khỏi repo maven cục bộ của tôi. Một phiên bản cũ của bouncycastle là thủ phạm (tôi biết tôi vừa thay đổi phiên bản và đó sẽ là vấn đề) và mặc dù phiên bản cũ không xuất hiện ở đâu trong những gì đang được xây dựng, nó đã giải quyết vấn đề của tôi. Tôi đã sử dụng intellij phiên bản 14 và sau đó nâng cấp lên 15 trong quá trình này.


1

Trong trường hợp của tôi, tôi gặp phải lỗi này theo cách này. pom.xmldự án của tôi xác định hai phụ thuộc AB. Và cả hai ABđược xác định phụ thuộc vào cùng một tạo phẩm (gọi nó C) nhưng các phiên bản khác nhau của nó ( C.1C.2). Khi điều này xảy ra, đối với mỗi lớp trong Cmaven chỉ có thể chọn một phiên bản của lớp từ hai phiên bản (trong khi xây dựng một uber-jar ). Nó sẽ chọn phiên bản "gần nhất" dựa trên các quy tắc hòa giải phụ thuộc của nó và sẽ đưa ra cảnh báo "Chúng tôi có một lớp trùng lặp ..." Nếu một chữ ký phương thức / lớp thay đổi giữa các phiên bản, nó có thể gây ra java.lang.IncompatibleClassChangeErrorngoại lệ nếu phiên bản không chính xác là được sử dụng trong thời gian chạy.

Nâng cao: Nếu Aphải sử dụng v1 CBphải sử dụng v2 C, thì chúng ta phải di chuyển C vào ABcác poms để tránh xung đột lớp (chúng ta có cảnh báo lớp trùng lặp) khi xây dựng dự án cuối cùng phụ thuộc vào cả hai AB.


1

Trong trường hợp của tôi, lỗi xuất hiện khi tôi thêm com.nimbusdsthư viện trong ứng dụng của mình được triển khai Websphere 8.5.
Ngoại lệ dưới đây đã xảy ra:

Nguyên nhân bởi: java.lang.IncompiverseClassChangeError: org.objectweb.asm.AnnotationVisitor

Giải pháp là loại trừ bình asm ra khỏi thư viện:

<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

Vui lòng kiểm tra xem mã của bạn không bao gồm hai dự án mô-đun có cùng tên lớp và định nghĩa gói. Ví dụ, điều này có thể xảy ra nếu ai đó sử dụng sao chép-dán để tạo triển khai giao diện mới dựa trên triển khai trước đó.


0

Nếu đây là bản ghi các sự cố có thể xảy ra của lỗi này thì:

Tôi vừa gặp lỗi này trên WAS (8.5.0.1), trong quá trình tải CXF (2.6.0) của cấu hình lò xo (3.1.1_release) trong đó BeanInstantiationException đã cuộn lên Phần mở rộng CXF, cuộn lên một IncompizableClassChangeError. Đoạn mã sau đây cho thấy ý chính của dấu vết ngăn xếp:

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

Trong trường hợp này, giải pháp là thay đổi thứ tự đường dẫn của mô-đun trong tệp chiến tranh của tôi. Đó là, mở ứng dụng chiến tranh trong bảng điều khiển WAS bên dưới và chọn (các) mô đun máy khách. Trong cấu hình mô-đun, đặt tải lớp là "cha mẹ cuối cùng".

Điều này được tìm thấy trong bảng điều khiển WAS:

  • Applicatoins -> Loại ứng dụng -> Ứng dụng WebSphere Enterprise
  • Nhấp vào liên kết đại diện cho ứng dụng của bạn (chiến tranh)
  • Nhấp vào "Quản lý mô-đun" trong phần "Mô-đun"
  • Nhấp vào liên kết cho (các) mô-đun bên dưới
  • Thay đổi "Thứ tự trình nạp lớp" thành "(cha mẹ cuối cùng)".

0

Tài liệu cho một kịch bản khác sau khi đốt quá nhiều thời gian.

Hãy chắc chắn rằng bạn không có một lọ phụ thuộc có một lớp có chú thích EJB trên đó.

Chúng tôi đã có một tệp jar chung có @localchú thích. Lớp đó sau đó đã được chuyển ra khỏi dự án chung đó và vào dự án jar ejb chính của chúng tôi. Bình ejb của chúng tôi và bình chung của chúng tôi đều được bó trong một tai. Phiên bản của phụ thuộc jar chung của chúng tôi đã không được cập nhật. Do đó, 2 lớp đang cố gắng trở thành một cái gì đó với những thay đổi không tương thích.


0

Tất cả những điều trên - vì bất kỳ lý do gì tôi đã thực hiện một số phép tái cấu trúc lớn và bắt đầu có được điều này. Tôi đã đổi tên gói giao diện của tôi và nó đã xóa nó. Mong rằng sẽ giúp.


0

Vì một số lý do, ngoại lệ tương tự cũng được đưa ra khi sử dụng JNI và truyền đối số j class thay vì công việc khi gọi a Call*Method().

Điều này tương tự như câu trả lời từ Ogre Psalm33.

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;
}

Tôi biết rằng hơi muộn để trả lời câu hỏi này 5 năm sau khi được hỏi nhưng đây là một trong những lượt truy cập hàng đầu khi tìm kiếm java.lang.IncompatibleClassChangeErrorvì vậy tôi muốn ghi lại trường hợp đặc biệt này.


0

Thêm 2 xu của tôi. Nếu bạn đang sử dụng scala và sbt và đăng nhập scala làm phụ thuộc, thì điều này có thể xảy ra vì phiên bản trước của scala-log có tên scala-logging-api.So; về cơ bản, độ phân giải phụ thuộc không xảy ra do khác nhau tên dẫn đến lỗi thời gian chạy trong khi khởi chạy ứng dụng scala.


0

Một nguyên nhân bổ sung của vấn đề này là nếu bạn đã Instant Runkích hoạt cho Android Studio.

Cách khắc phục

Nếu bạn thấy bạn bắt đầu nhận được lỗi này, hãy tắt Instant Run.

  1. Cài đặt chính của Android Studio
  2. Xây dựng, Thi công, Triển khai
  3. Chạy ngay lập tức
  4. Bỏ chọn "Kích hoạt chạy ngay lập tức ..."

Tại sao

Instant Runsửa đổi một số lượng lớn các thứ trong quá trình phát triển, để giúp cung cấp các bản cập nhật cho Ứng dụng đang chạy của bạn nhanh hơn. Do đó chạy ngay lập tức. Khi nó hoạt động, nó thực sự hữu ích. Tuy nhiên, khi một sự cố như đình công này, điều tốt nhất cần làm là tắt Instant Runcho đến khi phiên bản Android Studio tiếp theo phát hành.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.