线程 “main” java.lang.NoSuchMethodError 中的异常:java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer

2022-09-01 01:06:50

我有一个如下方法,它已经正常运行了很长时间:

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

经过一段时间的搜索并通过在8和11之间切换已安装的JDK进行验证,我发现ByteBuffer类中的几种方法(例如flip(),clear())有一些变化。(new overridden methods)

在Java 8中,在调用ByteBuffer类的方法时,由于它没有此方法的实现,因此它实际上是从扩展类Buffer调用该方法;返回的对象如下:flip()Buffer

缓冲区类中:

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

但是,在 Java 11 中,ByteBuffer 类已经实现了自己的 flip() 方法,并且返回的对象从 Buffer 更改为 ByteBuffer(此更改应从 Java 9 开始):

字节缓冲类中

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

由于我使用JDK 11(更高的JDK版本)来编译在Java 8上运行的程序,因此偶尔会遇到相应的javadoc异常:

但是,默认情况下,javac 针对最新版本的平台 API 进行编译。因此,编译的程序可能会意外地使用仅在当前版本的平台中可用的 API。此类程序无法在较旧版本的平台上运行,无论传递给 -source 和 -target 选项的值如何。这是一个长期的可用性痛点,因为用户希望通过使用这些选项,他们将获得可以在 -target 指定的平台版本上运行的类文件。

该声明可以在此处引用:http://openjdk.java.net/jeps/247



因此,要解决此类问题,有两种方法可以做到这一点:


方法 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) 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();
}

答案 2

推荐