Java SE 9 多版本兼容 JAR 包示例
說明
Java 9 版本中增強(qiáng)了Jar 包多版本字節(jié)碼文件格式支持,也就是說在同一個(gè) Jar 包中我們可以包含多個(gè) Java 版本的 class 文件,這樣就能做到 Jar 包升級(jí)到新的 Java 版本時(shí)不用強(qiáng)迫使用方為了使用新 Jar 包而升級(jí)自己的業(yè)務(wù)模塊 Java 版本,也不用針對(duì)不同最低支持 Java 版本提供不同的 Jar,真正的做到了一個(gè) Jar 包兼容所有的目的。
本文通過以下示例來說明多版本 Jar 包的使用。
環(huán)境準(zhǔn)備
機(jī)器上應(yīng)該有多個(gè)版本的 JDK 用于測(cè)試,并且至少有一個(gè)是 JDK 9 或者更高版本。
命令行編譯示例
注:本示例無需使用 IDE ,我們用最原始的方式創(chuàng)建一個(gè)多版本的 Jar 包。
新建一個(gè)文件夾,用項(xiàng)目名稱命名,并且在其中把 src 目錄,包名都建好,可以自定義,后續(xù)編譯命令自行調(diào)整即可。

src\main\java\git\snippet 目錄下存的是舊版本 JDK 編寫的代碼。在這個(gè)目錄下新建兩個(gè)類。
package git.snippet;
/**
* Java SE 9 Multi-Release JAR Files示例
*
* @author <a href="mailto:410486047@qq.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" >Grey</a>
* @date 2022/8/14
* @since 9
*/
public class App {
public static void main(String[] args) {
Helper.hello(args[0]);
}
}package git.snippet;
/**
* @author <a href="mailto:410486047@qq.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" >Grey</a>
* @date 2022/8/14
* @since 1.7
*/
public class Helper {
public static void hello(String name) {
// jdk 9+不能用_作為變量
String _ = "hello";
System.out.println(_ + ", " + name);
}
}src\main\java9\git\snippet 目錄下存的是新版本 JDK 編寫的代碼。我們需要把 Helper 類用新的 JDK 版本特性來實(shí)現(xiàn)。代碼如下
package git.snippet;
/**
* @author <a href="mailto:410486047@qq.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" >Grey</a>
* @date 2022/8/14
* @since 9
*/
public class Helper {
public static void hello(String name) {
// 舊版本用_作為變量,jdk9不能用_作為變量
String fixName = "hello";
System.out.println(fixName + ", " + name + " from jdk9");
}
}創(chuàng)建好上述類以后,項(xiàng)目結(jié)構(gòu)如下:

接下來是編譯,在項(xiàng)目目錄下,用 JDK 9+的 javac 執(zhí)行如下兩個(gè)編譯命令
C:\jdk\jdk-11\bin\javac --release 7 -d classes src\main\java\git\snippet\*.java
提示信息如下(僅顯示了警告)
D:\git\hello-mrjar>C:\jdk\jdk-11\bin\javac --release 7 -d classes src\main\java\git\snippet\*.java
src\main\java\git\snippet\Helper.java:11: 警告: 從發(fā)行版 9 開始, '_' 為關(guān)鍵字, 不能用作標(biāo)識(shí)符
String _ = "hello";
^
src\main\java\git\snippet\Helper.java:12: 警告: 從發(fā)行版 9 開始, '_' 為關(guān)鍵字, 不能用作標(biāo)識(shí)符
System.out.println(_ + ", " + name);
^
2 個(gè)警告C:\jdk\jdk-11\bin\javac --release 9 -d classes-9 src\main\java9\git\snippet\*.java
無提示信息和報(bào)錯(cuò)信息。
接下來是通過 JDK 9+ 的 jar 進(jìn)行打包,打包的時(shí)候,運(yùn)行如下打包命令
C:\jdk\jdk-11\bin\jar --create --file target/hello-mrjar.jar --main-class git.snippet.App -C classes . --release 9 -C classes-9 .
如果提示如下報(bào)錯(cuò)信息:
java.nio.file.NoSuchFileException: C:\Users\zhuiz\AppData\Local\Temp\hello-mrjar.jar9462053262887373909.jar -> target\hello-mrjar.jar
at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:85)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
at java.base/sun.nio.fs.WindowsFileCopy.move(WindowsFileCopy.java:395)
at java.base/sun.nio.fs.WindowsFileSystemProvider.move(WindowsFileSystemProvider.java:292)
at java.base/java.nio.file.Files.move(Files.java:1422)
at jdk.jartool/sun.tools.jar.Main.validateAndClose(Main.java:466)
at jdk.jartool/sun.tools.jar.Main.run(Main.java:349)
at jdk.jartool/sun.tools.jar.Main.main(Main.java:1681)
則手動(dòng)在項(xiàng)目目錄下建立一個(gè)target文件夾,再次執(zhí)行打包命令,錯(cuò)誤解決。
在 target 目錄下,包已經(jīng)打好 hello-mrjar.jar 。
最后進(jìn)行測(cè)試,用JDK 9之前的 java 來執(zhí)行這個(gè) jar 包。
C:\jdk\jdk1.8\bin\java -jar hello-mrjar.jar Grey
輸出如下
hello, Grey
用 JDK 9+ 的 java 來執(zhí)行這個(gè) jar 包。
C:\jdk\jdk-11\bin\java -jar hello-mrjar.jar Grey
輸出如下:
hello, Grey from jdk9
這樣就實(shí)現(xiàn)了同一個(gè) Jar 包中包含多個(gè) Java 版本的 class 文件,用不同版本 JDK 執(zhí)行的時(shí)候,運(yùn)行不同版本的 class 文件。
也可以使用 Intellij IDEA 來創(chuàng)建多版本 Jar,這里是參考文檔: Creating Multi-Release JAR Files in IntelliJ IDEA
Maven 項(xiàng)目配合多版本 Jar 示例
多數(shù)情況下,我們不會(huì)手動(dòng)創(chuàng)建項(xiàng)目目錄并編譯,一般用 Maven 來管理項(xiàng)目。本示例演示如何在 Maven 下進(jìn)行多版本 Jar 包的管理。
創(chuàng)建一個(gè) Maven 項(xiàng)目,結(jié)構(gòu)如下:

和上例類似, src\main\java9 文件夾中是對(duì)應(yīng)的新版本 JDK 的代碼
src\main\java 文件夾中是對(duì)應(yīng)的舊版本的 JDK 代碼。
代碼清單如下:
package git.snippet;
public class App {
public static void main(String[] args) {
System.out.println(String.format("Running on %s", new DefaultVersion().version()));
}
}舊版本代碼,放在 src\main\java 對(duì)應(yīng)的包下。
package git.snippet;
public class DefaultVersion {
public String version() {
System.out.println("use jdk");
return System.getProperty("java.version");
}
}新版本代碼,放在 src\main\java9 對(duì)應(yīng)的包下。
package git.snippet;
public class DefaultVersion {
public String version() {
System.out.println("use jdk 9+");
return Runtime.version().toString();
}
}pom.xml 文件配置,注意,相關(guān)的文件夾或者包有調(diào)整,需要做對(duì)應(yīng)的調(diào)整。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>git.snippet</groupId>
<artifactId>hello-mrjar-with-maven</artifactId>
<version>1.0</version>
<properties>
<java.version>11</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>compile-java-8</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compileSourceRoots>
<!---舊版本代碼的位置-->
<compileSourceRoot>${project.basedir}/src/main/java</compileSourceRoot>
</compileSourceRoots>
</configuration>
</execution>
<execution>
<id>compile-java-9</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<release>9</release>
<compileSourceRoots>
<!---新版本代碼的位置-->
<compileSourceRoot>${project.basedir}/src/main/java9</compileSourceRoot>
</compileSourceRoots>
<outputDirectory>${project.build.outputDirectory}/META-INF/versions/9</outputDirectory>
</configuration>
</execution>
<execution>
<id>default-testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<skip>true</skip>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
<configuration>
<archive>
<manifestEntries>
<Multi-Release>true</Multi-Release>
</manifestEntries>
<manifest>
<!--設(shè)置主方法入口-->
<mainClass>git.snippet.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>然后用新版本的 JDK 進(jìn)行打包,在項(xiàng)目目錄下執(zhí)行:
mvn clean package -Dmaven.test.skip=true
提示:
[INFO] --- maven-jar-plugin:3.2.0:jar (default-jar) @ hello-mrjar-with-maven --- [INFO] Building jar: D:\git\hello-mrjar-with-maven\target\hello-mrjar-with-maven-1.0.jar [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.447 s [INFO] Finished at: 2022-08-15T11:29:48+08:00 [INFO] ------------------------------------------------------------------------
說明打包成功。然后進(jìn)入 target 目錄,進(jìn)行驗(yàn)證
用舊版本的 Java 執(zhí)行 Jar 包
C:\jdk\jdk1.8\bin\java -jar hello-mrjar-with-maven-1.0.jar 復(fù)制代碼
輸出:
use jdk
Running on 1.8.0_202
用新版本的 Java 執(zhí)行 Jar 包
C:\jdk\jdk-11\bin\java -jar hello-mrjar-with-maven-1.0.jar
輸出:
use jdk 9+
Running on 11.0.15+8-LTS-149
到此這篇關(guān)于Java SE 9 多版本兼容 JAR 包示例的文章就介紹到這了,更多相關(guān)Java JAR 包內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于Javascript中defer和async的區(qū)別總結(jié)
相信看過javascript高級(jí)程序設(shè)計(jì)的人,在javascript高級(jí)程序設(shè)計(jì)里,應(yīng)該看到了介紹了有關(guān)defer和async的區(qū)別,可是比較淺顯,而且也說得不是很清楚。下面我們來通過這篇文章來詳細(xì)了解下dfer和async的區(qū)別。2016-09-09
基于javascript實(shí)現(xiàn)最簡(jiǎn)單選項(xiàng)卡切換
這篇文章主要為大家詳細(xì)介紹了基于javascript實(shí)現(xiàn)最簡(jiǎn)單選項(xiàng)卡切換,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02
JS實(shí)現(xiàn)判斷滾動(dòng)條滾到頁(yè)面底部并執(zhí)行事件的方法
這篇文章主要介紹了JS實(shí)現(xiàn)判斷滾動(dòng)條滾到頁(yè)面底部并執(zhí)行事件的方法,本文先是分析了需求以及必備知識(shí),然后給出實(shí)現(xiàn)代碼,需要的朋友可以參考下2014-12-12
微信小程序天氣預(yù)報(bào)功能實(shí)現(xiàn)(支持自動(dòng)定位,附源碼)
對(duì)于一個(gè)經(jīng)常出門在外的人,關(guān)注天氣是至關(guān)重要的,下面這篇文章主要給大家介紹了關(guān)于微信小程序天氣預(yù)報(bào)功能實(shí)現(xiàn)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),支持自動(dòng)定位,需要的朋友可以參考下2022-04-04
JavaScript學(xué)習(xí)筆記之取數(shù)組中最大值和最小值
在實(shí)際業(yè)務(wù)中有的時(shí)候要取出數(shù)組中的最大值或最小值。但在數(shù)組中并沒有提供arr.max()和arr.min()這樣的方法。那么是不是可以通過別的方式實(shí)現(xiàn)類似這樣的方法呢?那么今天我們就來整理取出數(shù)組中最大值和最小值的一些方法,需要的朋友一起學(xué)習(xí)吧2016-03-03

