首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在带有Java 8目标的后续版本中,javac 11链接方法应该如何重写?

在带有Java 8目标的后续版本中,javac 11链接方法应该如何重写?
EN

Stack Overflow用户
提问于 2020-11-17 03:32:02
回答 1查看 1.1K关注 0票数 11

假设我使用的是javac,但我使用的是设置为1.8--source--target选项,因此我的源代码将被视为Java8,输出.class文件将与Java8兼容。我的目标是生成能够在Java8JVM上运行的.class文件。

假设我编写了以下Java 8代码:

代码语言:javascript
复制
import java.nio.ByteBuffer;

…

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

问题是:Java11 javac应该在.class文件中放置什么来链接byteBuffer.flip()方法调用?在回答之前,请考虑以下几点:

  • ByteBufferByteBuffer都没有在ByteBuffer级别声明flip()方法。
  • ByteBufferBuffer的一个直接子类。在两个版本的Buffer.flip() API中都声明了BufferBuffer.flip()
  • 在Java8源代码中,没有ByteBuffer.flip()方法。
  • 但是在Java11源代码中,ByteBuffer重写了Buffer.flip()方法,如下所示。其目的显然是使用协方差,以便在Java11中,ByteBuffer.flip()可以方便地返回一个ByteBuffer而不是一个Buffer。@重写公共ByteBuffer触发器(){ super.flip();返回此;}

因此,要重申这个问题:Java11 javac (将--source--target选项设置为1.8 )是否应该生成一个链接到Buffer.flip()ByteBuffer.flip().class文件?如果是前者,那么它怎么知道不包含ByteBuffer.flip(),因为(Java8)代码清楚地引用了ByteBuffer.flip(),而(Java11)编译器看到了运行时中有一个ByteBuffer.flip()方法?但是如果是后者,那么我怎么知道我的100%正确的Java8兼容源代码(使用Java 11编译时)即使我使用--source--target选项来指示Java8,也会在Java8JRE上运行?(请注意,OpenJDK 11.0.5似乎选择了后一个选项。但哪一个是正确的?)

(请注意,我松散地使用了"link“这个词;我目前对生成的字节码并不熟悉。我只知道类文件以某种方式引用了Buffer.flip()ByteBuffer.flip();如果在运行时找不到这个方法,JVM将抛出一个异常,例如:java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer;)。

作为一个额外的问题,我想知道使用--release选项set for Java8是否会改变答案。但是请注意,我不能使用--release选项(相当于Maven <release>编译器插件选项),因为我希望使用Java8和Java11构建Maven项目。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-11-17 04:47:54

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

Java源代码

代码语言:javascript
复制
ByteBuffer byteBuffer = ByteBuffer.allocate(64);
byteBuffer.flip();

Java 8字节码

代码语言:javascript
复制
 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字节码

代码语言:javascript
复制
 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

如您所见,这两种方法都“链接”到ByteBufferByteBuffer方法,即使该方法不是在Java8中声明的。

但是,在字节码级别,方法签名包括返回类型。这意味着JVM支持语言,在这些语言中,即使Java不支持返回类型不同的方法,也可以重载这些方法。

该方法的Java11版本有一个不同的返回类型,可以在()之后的“链接”方法中看到,其中Java8显示返回类型为Ljava/nio/Buffer;,Java11显示返回类型为Ljava/nio/ByteBuffer;

当您使用针对Java11RuntimeLibrary编译的代码,并尝试在Java8上运行它时,您将得到Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer;

这就是为什么您应该始终指定引导类路径来指向与目标Java版本匹配的Java。当您使用Java11的javac和选项-source 8 -target 8进行编译时,它实际上会警告您:

代码语言:javascript
复制
warning: [options] bootstrap class path not set in conjunction with -source 8
1 warning

这就是为什么他们实现了更新的--release <release>选项来代替-source-target。如果使用--release 8编译,生成的.class文件将在Java8上运行而不会出错。

更新

您不需要安装Java 8来使用选项--release 8。Java 11安装知道Java 8运行时库的方法是什么。

--release选项是在Java9中实现的,这是JEP 247:为较早的平台版本编译的结果,它说:

对于JDK和--release M,M< N,需要平台M版本的文档化API的签名数据。此数据存储在$JDK_ROOT/lib/ct.sym文件中,该文件与JDK 8中同名的文件相似,但并不相同。ct.sym文件是一个ZIP文件,包含与目标平台版本的类文件相对应的被剥离的类文件。

票数 16
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64868952

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档