Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer

Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer

我有一个下面的方法已经 运行 正确使用了很长时间:

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)

有人对此有任何想法吗?

程序运行ning环境规范是这样的:

服务器:

  1. 打开jdk版本“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>

经过一段时间的搜索并通过在 8 和 11 之间切换安装的 JDK 进行验证,我发现有一些更改 (new overridden methods) 应用于几种方法(例如 flip(), clear() ) 在 ByteBuffer class.

在Java8中,在调用ByteBuffer的flip()方法时class,由于没有实现该方法,所以实际上是从扩展[=59中调用方法=], 缓冲区;返回 Buffer 对象如下:

缓冲区 class:

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

但是在Java11,ByteBufferclass实现了自己的flip()方法,返回对象由Buffer变成了ByteBuffer(这个变化应该是从Java 9):

ByteBuffer class:

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

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

By default, however, javac compiles against the most-recent version of the platform APIs. The compiled program can therefore accidentally use APIs only available in the current version of the platform. Such programs cannot run on older versions of the platform, regardless of the values passed to the -source and -target options. This is a long-term usability pain point, since users expect that by using these options they'll get class files that can run on the the platform version specified by -target.

声明可参考这里:http://openjdk.java.net/jeps/247



所以,为了解决这类问题,有两种方法:


方法一

通过使用新引入的命令行选项,可以在编译期间处理:

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

方法二

或者我们可以在代码中处理它,作为预防方法,通过在调用相应方法之前显式地将 ByteByffer 转换为 Buffer:

((Buffer) bb).flip();

为了强制它调用扩展的 class 方法(如果编译过程没有考虑新的命令行选项):

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