假设我使用的是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
水平。 ByteBuffer
是 Buffer
. 有两个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构建。
1条答案
按热度按时间rqenqsqc1#
如果我们采用下面的代码并用Java8和Java11编译,我们将得到下面的字节码,如运行时所见
javap -c MyClass.class
.java源代码
java 8字节码
java 11字节码
如您所见,它们都“链接”到
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
,它实际上会警告您:这就是为什么他们实施了新的
--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文件,包含与目标平台版本的类文件相对应的精简类文件。