【问题标题】:Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer线程“主”java.lang.NoSuchMethodError 中的异常:java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer
【发布时间】:2020-07-30 17:03:52
【问题描述】:

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

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

但是,今天我在服务器上编译并运行程序后,启动程序时记录了以下异常。它显示未找到翻转()方法:

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>

【问题讨论】:

  • 开发时为什么不能使用自己服务器的同一个jdk?
  • 是的,你是对的,这将是理想的情况,应该在生产环境中完成。但是对于非生产环境的某些情况,例如我们不时在同一个 IDE 工作区中开发不同的宠物项目,甚至在与其他项目在其上运行的共享环境中运行。偶尔会遇到。
  • 我在使用 Apache NetBeans 12.6 和 jdk 1.8.0_202 时遇到了同样的错误。我通过将 Apache NetBeans 降级到 12.3 解决了这个问题

标签: java java-8 java-11 bytebuffer


【解决方案1】:

搜索了一会,通过在8和11之间切换安装的JDK验证,我发现(new overridden methods)应用于几个方法(例如flip(),clear() ) 在 ByteBuffer 类中。

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

Buffer类中:

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

但是在Java 11中,ByteBuffer类已经实现了自己的flip()方法,返回的对象从Buffer变成了ByteBuffer(这个变化应该从Java 9开始):

ByteBuffer 类中:

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

【讨论】:

  • 方法 3 只是使用用于执行的相同 jdk 进行开发
  • 这就是 --release 选项的用途。无需自己指定这三个不同的选项。这就是您链接的页面所描述的内容。
  • 我已经将很多项目转换为使用 JDK 9+(主要是 11)构建,针对新旧(8 或更低)的运行时 JVM,并且发布标志有了很大的改进。最大的改进是,如果您使用不在目标 JVM 中的库/方法,则会出现编译错误。我强烈建议使用它(选项 1)。以防万一不清楚如何用 maven 做到这一点。您删除 maven-compiler-plugin 中的源/目标行(使用最新版本)并将它们替换为单个“发布”元素,而不是指定您想要运行的目标 jvm(在您的情况下看起来像 8)。
  • @Matthieu 你是什么意思?此代码使用 1.8 版编译。当您想使用较新版本进行编译但保持兼容性时,请使用--release 选项。这就是它的用途。为什么它必须是魔法?
  • @MarkJeronimus Eclipse 确实支持 --release 选项。即使有错误也生成类文件的特性完全不相关。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-09
  • 2021-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多