1. 概述
在这个教程中,我们将了解Java 9的新命令行选项--release
。使用--release N
选项运行的Java编译器会自动生成与Java版本N兼容的类文件。我们将讨论这个选项如何与现有的编译命令行选项-source
和-target
相关联。
2. 需要--release
选项的情况
为了理解--release
选项的必要性,考虑这样一个场景:我们需要使用Java 8编译代码,并希望生成的类文件能兼容Java 7。
在Java 9之前,通过设置-source
和-target
选项可以实现这一点,其中:
-source
: 指定编译器接受的Java版本-target
: 指定生成的类文件的Java版本
假设编译后的程序只使用当前平台(在这种情况下是Java 8)特有的API。那么,无论-source
和-target
选项设置为何值,编译后的程序都无法在像Java 7这样的早期版本上运行。
此外,为了在Java 8及以下版本工作,我们需要同时添加-bootclasspath
选项。
为了简化跨编译问题,Java 9引入了新的--release
选项来简化过程。
3. 与-source
和-target
选项的关系
根据JDK定义,--release N
可以扩展为:
对于N < 9:
-source
N-target
N-bootclasspath
<从N获取的文档化API>对于N >= 9:
-source
N-target
N--system
<从N获取的文档化API>-bootclasspath
: 一个以分号分隔的目录列表、JAR存档和ZIP存档,用于搜索启动类文件--system
: 替换Java 9及更高版本的系统模块位置
对于Java版本N < 9,这些API包括从位于jre/lib/rt.jar
和其他相关jar中的启动类文件。
对于Java版本N >= 9,这些API包括从jdkpath/jmods/
目录下的Java模块获取的启动类文件。
首先,我们创建一个示例类并使用Java 9中重写的flip
方法:
import java.nio.ByteBuffer;
public class TestForRelease {
public static void main(String[] args) {
ByteBuffer bb = ByteBuffer.allocate(16);
bb.flip();
System.out.println("Baeldung: --release option test is successful");
}
}
4.1. 使用现有的-source
和-target
选项
使用-source
和-target
选项值为8在Java 9中编译代码:
/jdk9path/bin/javac TestForRelease.java -source 8 -target 8
结果成功,但伴有警告:
warning: [options] bootstrap class path not set in conjunction with -source 8
1 warning
现在,我们在Java 8上运行我们的代码:
/jdk8path/bin/java TestForRelease
我们看到这失败了:
Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer;
at com.corejava.TestForRelease.main(TestForRelease.java:9)
如我们所见,这并不是我们期望的结果,尽管我们在--release
和-target
选项中指定了8。尽管编译器应该考虑到这一点,但事实并非如此。
让我们详细了解一下。
在Java 9之前,Buffer
类包含flip
方法:
public Buffer flip() {
...
}
而在Java 9中,ByteBuffer
(它扩展了Buffer
)重写了flip
方法:
@Override
public ByteBuffer flip() {
...
}
当这个新方法在Java 9上编译并在Java 8上运行时,由于两个方法返回类型不同,运行时方法查找使用描述符失败,导致错误:
Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer;
at com.corejava.TestForRelease.main(TestForRelease.java:9)
在编译过程中,我们得到了先前忽略的警告。这是因为Java编译器默认使用最新的API进行编译。即使我们指定了-source
和-target
为8,编译器仍然使用了Java 9的类,因此我们的程序无法在Java 8上运行。
因此,我们必须向Java编译器传递另一个名为-bootclasspath
的命令行选项来选择正确的版本。
现在,让我们使用-bootclasspath
选项重新编译相同的代码:
/jdk9path/bin/javac TestForRelease.java -source 8 -target 8 -Xbootclasspath ${jdk8path}/jre/lib/rt.jar
这次,编译结果成功,而且没有警告。
现在,我们在Java 8上运行我们的代码,看到它是成功的:
/jdk8path/bin/java TestForRelease
Baeldung: --release option test is successful
虽然现在跨编译工作正常,但我们需要提供三个命令行选项。
4.2. 使用--release
选项
现在,让我们使用--release
选项编译相同的代码:
/jdk9path/bin/javac TestForRelease.java —-release 8
同样,这次编译也成功了,没有警告。
最后,当我们用Java 8运行代码时,我们看到它是成功的:
/jdk8path/bin/java TestForRelease
Baeldung: --release option test is successful
我们看到,使用--release
选项非常直接,因为javac
在内部设置了正确的-source
、-target
和-bootclasspath
值。
5. 在Maven编译插件中的使用
通常,我们会使用Maven或Gradle等构建工具,而不是命令行javac
工具。所以,在这一部分,我们将看看如何在Maven编译插件中应用--release
选项。
首先,我们来看看如何使用现有的-source
和-target
选项:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.12.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
接下来,这是如何使用--release
选项的方法:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.12.1</version>
<configuration>
<release>1.8</release>
</configuration>
</plugin>
</plugins>
尽管行为与之前描述的相同,但我们传递这些值给Java编译器的方式有所不同。
6. 总结
在这篇文章中,我们了解了--release
选项及其与现有-source
和-target
选项的关系。然后,我们看到了如何在命令行和Maven编译插件中使用这个选项。
最后,我们看到新的--release
选项在跨编译时需要的输入选项更少。因此,建议在可能的情况下优先使用它,而不是-target
、-source
和-bootclasspath
选项。