java - javac 11 应该如何使用 Java 8 目标在更高版本中覆盖方法?

标签 java java-8 java-11

假设我正在使用 Java 11 javac ,但我正在使用 --source--target选项设置为 1.8这样我的源代码将被视为 Java 8 和输出 .class文件将与 Java 8 兼容。我的目标是生成 .class可以在 Java 8 JVM 上运行的文件。
假设我正在编译以下 Java 8 代码:

import java.nio.ByteBuffer;

…

ByteBuffer byteBuffer = …; //init somehow
byteBuffer.flip(); //what ends up in the `.class` file?
问题是:Java 11 javac 应该是什么?放入 .class文件链接 byteBuffer.flip()方法调用?在回答之前,请考虑以下问题:
  • 都没有 Java 8 ByteBuffer 也不是 Java 11 ByteBuffer 声明 flip()方法在 ByteBuffer等级。
  • ByteBufferBuffer 的直接子类.两者都有Java 8 Buffer.flip() 和一个 Java 11 Buffer.flip() Buffer 中声明两个版本的 API。
  • 在 Java 8 源代码中,没有 ByteBuffer.flip()方法。
  • 但是在 Java 11 源代码中,ByteBuffer覆盖 Buffer.flip()方法如下。目的显然是使用协方差,以便在 Java 11 ByteBuffer.flip() 中会方便地返回 ByteBuffer而不是 Buffer .
    @Override
    public ByteBuffer flip() {
        super.flip();
        return this;
    }
    

  • 所以重申这个问题:Java 11 是否应该javac , 与 --source--target选项设置为 1.8 ,生成 .class链接到 Buffer.flip() 的文件或到 ByteBuffer.flip() ?如果是前者,那怎么知道不包括ByteBuffer.flip()相反,因为 (Java 8) 代码清楚地引用了 ByteBuffer.flip()并且(Java 11)编译器看到有一个 ByteBuffer.flip()运行时的方法?但如果是后者,那么我怎么知道我的 100% 正确的 Java 8 兼容源代码,当使用 Java 11 编译时,即使我使用 --source 也会在 Java 8 JRE 上运行和 --target指示 Java 8 的选项? (请注意,OpenJDK 11.0.5 似乎选择了后者。但哪个是正确的?)
    (请注意,我松散地使用了“链接”这个词;我目前不熟悉生成什么字节码。我所知道的是类文件以某种方式引用了 Buffer.flip()ByteBuffer.flip() ;和如果在运行时找不到此方法,JVM 将抛出异常,例如:java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer;。)
    作为奖励问题,我想知道是否使用 --release为 Java 8 设置的选项会改变答案。但请注意,我不能使用 --release选项(相当于 Maven <release> 编译器插件选项),因为我希望我的 Maven 项目可以使用 Java 8 和 Java 11 构建。

    最佳答案

    如果我们使用以下代码并使用 Java 8 和 Java 11 进行编译,我们将得到以下字节码,如运行 javap -c MyClass.class 时所见。 .
    Java源代码

    ByteBuffer byteBuffer = ByteBuffer.allocate(64);
    byteBuffer.flip();
    
    Java 8 字节码
     0: bipush        64
     2: invokestatic  #19   // Method java/nio/ByteBuffer.allocate:(I)Ljava/nio/ByteBuffer;
     5: astore_1
     6: aload_1
     7: invokevirtual #25   // Method java/nio/ByteBuffer.flip:()Ljava/nio/Buffer;
    10: pop
    
    Java 11 字节码
     0: bipush        64
     2: invokestatic  #19   // Method java/nio/ByteBuffer.allocate:(I)Ljava/nio/ByteBuffer;
     5: astore_1
     6: aload_1
     7: invokevirtual #25   // Method java/nio/ByteBuffer.flip:()Ljava/nio/ByteBuffer;
    10: pop
    
    如您所见,它们都“链接”到 flip() ByteBuffer的方法,即使该方法未在 Java 8 中声明。
    但是,在字节码级别,方法签名包括返回类型。这意味着 JVM 支持可以重载仅返回类型不同的方法的语言,即使 Java 不支持。
    Java 11 版本的方法有不同的返回类型,可以在“linked”方法中看到,后面是 () ,其中 Java 8 将返回类型显示为 Ljava/nio/Buffer; Java 11 将返回类型显示为 Ljava/nio/ByteBuffer; .
    当您使用针对 Java 11 运行时库编译的代码并尝试在 Java 8 上运行它时,您会得到 Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer;这就是为什么你应该总是指定 引导类路径 指向与目标 Java 版本匹配的 Java 运行时库。当您使用 Java 11 编译时 javac带选项 -source 8 -target 8 ,它实际上会警告你:
    warning: [options] bootstrap class path not set in conjunction with -source 8
    1 warning
    
    这就是为什么他们实现了更新的 --release <release>替换选项 -source-target .如果你用 --release 8 编译,生成 .class文件将在 Java 8 上运行而不会出错。

    更新
    您不需要安装 Java 8 即可使用选项 --release 8 . Java 11 安装知道 Java 8 运行时库的方法是什么。--release作为 JEP 247: Compile for Older Platform Versions 的结果,选项在 Java 9 中实现,其中说:

    For JDK N and --release M, M < N, signature data of the documented APIs of release M of the platform is needed. This data is stored in the $JDK_ROOT/lib/ct.sym file, which is similar, but not the same, as the file of the same name in JDK 8. The ct.sym file is a ZIP file containing stripped-down class files corresponding to class files from the target platform versions.

    关于java - javac 11 应该如何使用 Java 8 目标在更高版本中覆盖方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64868952/

    相关文章:

    java - Hibernate 搜索前缀

    Java 8 Flatmap 从集合中解开多个对象

    java - 为什么不使用自定义比较器从TreeSet中删除,则会删除较大的项集?

    java - 如何解决java.lang.NoClassDefFoundError : javax/xml/bind/JAXBException

    java - 从 Java EE/PHP 到 Rails 3 的应用程序迁移 - 模型

    java - 我应该如何让未经检查的异常冒泡并记录下来?

    java - Spring Boot SSL 和 keystore 证书安装

    java - 如何解决此错误 "Type mismatch: cannot convert from List<String> to Iterator<String>"

    java - 在Java中获取带有国家代码的地区的时区

    java - 使用流对内部对象的 Map 属性进行分组?