javac11应该如何使用java8目标在更高版本中重写链接方法?

mu0hgdu0  于 2021-07-12  发布在  Java
关注(0)|答案(1)|浏览(416)

假设我使用的是Java11 javac ,但我用的是 --source 以及 --target 选项设置为 1.8 因此,我的源代码将被视为Java8和输出 .class 文件将与Java8兼容。我的目标是生产 .class 可以在Java8JVM上运行的文件。
假设我正在编译以下java 8代码:

import java.nio.ByteBuffer;

…

ByteBuffer byteBuffer = …; //init somehow
byteBuffer.flip(); //what ends up in the `.class` file?

问题是:Java11应该做什么 javac 放入 .class 文件以链接 byteBuffer.flip() 方法调用?在回答之前,请考虑以下问题:
也不是java 8 ByteBuffer 也不是java 11 ByteBuffer 宣布 flip() 方法 ByteBuffer 水平。 ByteBufferBuffer . 有两个Java8 Buffer.flip() 和java 11 Buffer.flip() 声明于 Buffer 两个版本的api。
在Java8源代码中,没有 ByteBuffer.flip() 方法。
但是在Java11源代码中, ByteBuffer 覆盖 Buffer.flip() 方法如下。其目的显然是使用协方差,以便在Java11中 ByteBuffer.flip() 会很方便地返回 ByteBuffer 而不是 Buffer .

@Override
public ByteBuffer flip() {
    super.flip();
    return this;
}

所以重申一下这个问题:Java11 javac ,与 --source 以及 --target 选项设置为 1.8 ,生成 .class 链接到的文件 Buffer.flip() 或者 ByteBuffer.flip() ? 如果是前者,那它怎么知道不包括 ByteBuffer.flip() 相反,正如(Java8)代码清楚地引用 ByteBuffer.flip() (java11)编译器发现 ByteBuffer.flip() 运行时中的方法?但是如果是后者,那么我怎么知道我的100%正确的java8兼容源代码,当使用java11编译时,即使我使用 --source 以及 --target 表示Java8的选项(请注意,openjdk11.0.5似乎选择了后一个选项。但哪个是正确的?)
(请注意,我使用的是松散的“链接”一词;我目前还不太精通字节码是如何生成的。我只知道类文件以某种方式引用了 Buffer.flip() 或者 ByteBuffer.flip() ; 如果在运行时找不到这个方法,jvm将抛出一个异常,例如: java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer; .)
作为一个额外的问题,我想知道是否使用 --release 为Java8设置的选项将更改答案。但请注意,我不能使用 --release 选项(相当于maven <release> 编译器插件选项),因为我希望我的maven项目可以用java8和java11构建。

rqenqsqc

rqenqsqc1#

如果我们采用下面的代码并用Java8和Java11编译,我们将得到下面的字节码,如运行时所见 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 ,即使没有在那里为Java8声明方法。
但是,在字节码级别,方法签名包括返回类型。这意味着jvm支持只在返回类型上不同的重载方法的语言,即使java不支持这种语言。
java11版本的方法有一个不同的返回类型,这可以在下面的“linked”方法中看到 () ,其中Java8将返回类型显示为 Ljava/nio/Buffer; Java11将返回类型显示为 Ljava/nio/ByteBuffer; .
当您获取针对Java11运行时库编译的代码,并尝试在Java8上运行它时,您会得到 Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer; 这就是为什么您应该始终指定引导类路径,以指向与目标java版本匹配的java运行时库。使用Java11编译时 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 文件将在Java8上正常运行。
更新
您不需要安装Java8就可以使用这个选项 --release 8 . Java11安装程序知道Java8运行时库的方法是什么。
这个 --release 选项是在java 9中实现的,这是jep 247:compile for older platform versions的结果,它说:
对于jdk n和 --release m、 m<n,需要平台m版本的API文件的签名数据。这些数据存储在 $JDK_ROOT/lib/ct.sym 文件,与jdk8中同名的文件类似,但不相同。这个 ct.sym 文件是一个zip文件,包含与目标平台版本的类文件相对应的精简类文件。

相关问题