Исключение в потоке "main" java .lang.NoSuchMethodError: java .nio.ByteBuffer.flip () Ljava / nio / ByteBuffer - PullRequest
2 голосов
/ 17 апреля 2020

У меня есть метод, описанный ниже, который долго работал правильно:

private String loadFromFile(){

    RandomAccessFile inFile = null;
    FileChannel inChannel = null;
    StringBuilder sb = new StringBuilder();
    try {

        inFile = new RandomAccessFile(this.latestImageFile, "r");
        inChannel = inFile.getChannel();

        ByteBuffer bb = ByteBuffer.allocate(2046);
        while( inChannel.read(bb) != -1){
            bb.flip();

            while(bb.hasRemaining()){
                char c = (char) bb.get();   // read character at current position and set the pointer to current position + 1
                sb.append(c);
            }

            bb.clear();
        }

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (inChannel != null) try {inChannel.close(); } catch (IOException e){}
        if (inFile != null ) try { inFile.close(); } catch (IOException e) {}
    }

    return sb.toString();
}

Однако, сегодня, после того как я скомпилировал и запустил программу на сервере, при запуске было зарегистрировано исключение ниже программа. Он показывает, что метод flip () не найден:

Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer;
        at com.rt.stream.s.exch.OBWorker.loadFromFile(OBWorker.java:271)
        at com.rt.stream.s.exch.OBWorker.initAndLoadOBs(OBWorker.java:184)
        at com.rt.stream.s.exch.OBWorker.<init>(OBWorker.java:145)
        at com.rt.stream.s.exch.OBWorkerMgr.initFromProperties(OBWorkerMgr.java:217)
        at com.rt.stream.s.exch.OBWorkerMgr.init(OBWorkerMgr.java:132)
        at com.rt.stream.s.exch.OBWorkerMgr.main(OBWorkerMgr.java:511)

Кто-нибудь есть идеи, пожалуйста?

Спецификация среды выполнения программы выглядит следующим образом:

Сервер:

  1. openjdk версия "1.8.0_242"

Разработка:

  1. IDE: Версия: 2019-09 R (4.13.0)

  2. JDK : jdk-11.0.1

  3. maven: apache -maven-3.3.3 (с примененной ниже конфигурацией)

   <source>1.8</source>   
   <target>1.8</target>

1 Ответ

4 голосов
/ 17 апреля 2020

После поиска и проверки путем переключения установленного JDK между 8 и 11 я обнаружил, что есть некоторые изменения (new overridden methods), примененные к нескольким методам (например, flip (), clear () ) в классе ByteBuffer.

В Java 8 при вызове метода filp() класса ByteBuffer, поскольку у него нет реализации для этого метода, поэтому он фактически вызывает метод из расширенного класса, Buffer; который возвращает Buffer объект, как показано ниже:

В Буфер класс:

public final Buffer flip() {
    limit = position;
    position = 0;
    mark = -1;
    return this;
}

Однако в Java 11 класс ByteBuffer реализовал свой собственный флип (), и возвращаемый объект изменяется с Buffer на ByteBuffer (это изменение следует начинать с Java 9):

In ByteBuffer класс:

ByteBuffer flip() {
    super.flip();
    return this;
}


Поскольку я использую JDK 11 (более поздняя версия JDK) для компиляции программы для запуска на Java 8, упомянутое исключение иногда встречалось бы в соответствии с javado c:

По умолчанию, однако, javac компилируется с самой последней версией API платформы. Поэтому скомпилированная программа может случайно использовать API-интерфейсы, доступные только в текущей версии платформы. Такие программы не могут работать на более старых версиях платформы, независимо от значений, переданных опциям -source и -target. Это долговременная проблема удобства использования, так как пользователи ожидают, что с помощью этих опций они получат файлы классов, которые могут работать на версии платформы, указанной в -target.

Оператор может быть здесь указано: http://openjdk.java.net/jeps/247



Итак, для решения такого рода проблем есть 2 способа сделать это:


Подход 1

Можно обрабатывать во время компиляции, используя недавно введенный параметр командной строки:

i.e.
javac --release N <source files>

which is equals to:
for N < 9: -source N -target N -bootclasspath <documented-APIs-from-N>,  
for N >= 9: -source N -target N --system <documented-APIs-from-N>.  


Подход 2

Или мы можем обработать его в кодах в качестве методов предосторожности, явно приведя ByteByffer к типу Buffer перед вызовом соответствующих методов:

((Buffer) bb) .flip ();

, чтобы заставить его вызывать метод расширенного класса (в случае, если процесс компиляции не учел новые параметры командной строки):

private String loadFromFile(){

    RandomAccessFile inFile = null;
    FileChannel inChannel = null;
    StringBuilder sb = new StringBuilder();
    try {

        inFile = new RandomAccessFile(this.latestImageFile, "r");
        inChannel = inFile.getChannel();

        ByteBuffer bb = ByteBuffer.allocate(2046);
        while( inChannel.read(bb) != -1){
            ((Buffer)bb).flip(); // explicitly casting

            while(bb.hasRemaining()){
                char c = (char) bb.get();
                sb.append(c);
            }

            ((Buffer) bb).clear(); // explicitly casting
        }

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (inChannel != null) try {inChannel.close(); } catch (IOException e){}
        if (inFile != null ) try { inFile.close(); } catch (IOException e) {}
    }

    return sb.toString();
}
...