JDK 8升級高版本JDK指南

沒耐心的可以直接跳到後面的實戰

準備工作

下載新版本的JDK

從Java SE 下載下載並安裝新的 JDK 版本。

在重新編譯之前執行你的程式

嘗試在最新的 JDK 版本 上執行您的應用程式。大多數程式碼和庫無需任何更改即可在 新版本上執行,但可能有一些庫需要升級。

當您執行應用程式時,請檢視來自 JVM 的有關過時 VM 選項的警告。如果 VM 無法啟動,則查詢Removed GC Options。

如果您的應用程式成功啟動,請仔細檢視您的測試並確保其行為與您一直使用的 JDK 版本相同。例如,一些早期採用者注意到他們的日期和貨幣格式不同。請參閱預設使用 CLDR 區域設定資料。

要使您的程式碼在最新的 JDK 版本上執行,請了解每個 JDK 版本中的新功能和更改。

有關 JDK 17 中的新功能和更改的詳細資訊,請參閱JDK 17 中的新增功能 - 新功能和增強功能。

有關 JDK 16 中的新功能和更改的詳細資訊,請參閱JDK 16 中的新增功能 - 新功能和增強功能。

有關 JDK 15 中的新功能和更改的詳細資訊,請參閱JDK 15 中的新增功能 - 新功能和增強功能。

有關 JDK 14 中的新功能和更改的詳細資訊,請參閱JDK 14 中的新增功能 - 新功能和增強功能。

有關 JDK 13 中的新功能和更改的詳細資訊,請參閱JDK 13 中的新增功能 - 新功能和增強功能。

有關 JDK 12 中的新功能和更改的詳細資訊,請參閱JDK 12 中的新增功能 - 新功能和增強功能。

有關 JDK 11 中的新功能和更改的詳細資訊,請參閱JDK 11 中的新增功能 - 新功能和增強功能。

有關 JDK 10 中的新功能和更改的詳細資訊,請參閱 JDK 10 中的新增功能。

有關 JDK 9 的所有新功能的完整列表,請參閱 JDK 9 中的新增功能。

有關 JDK 9 中更改的詳細資訊,請參閱JDK 9 發行說明。

即使您的程式看起來執行成功,您也應該完成本指南中的其餘步驟並檢視問題列表。

更新第三方庫

對於您使用的每個工具和第三方庫,您可能需要一個支援最新 JDK 版本的更新版本。

檢查第三方庫和工具供應商的網站,瞭解每個庫或工具的版本,這些庫或工具設計用於最新的 JDK。如果存在,則下載並安裝新版本。

如果您使用 Maven 或 Gradle 構建應用程式,請確保升級到支援最新 JDK 版本的最新版本。

如果您使用 IDE 開發應用程式,那麼它可能有助於遷移現有程式碼。NetBeans、Eclipse 和 IntelliJ IDE 都有可用的版本,包括對最新 JDK 的支援。

您可以在 OpenJDK wiki 上的 Quality Outreach上檢視使用 OpenJDK 構建的許多免費開源軟體 (FOSS) 專案的測試狀態。

如果需要,編譯您的應用程式

使用新版本的 JDK 編譯器編譯您的程式碼將簡化向新版本的遷移,因為程式碼可能依賴於已被確定為有問題的 API 和特性。但是,這不是絕對必要的。

如果您需要使用 JDK 11 和更高版本的編譯器編譯程式碼,請注意以下幾點:

如果您(“_”)在原始碼中使用下劃線字元作為單字元識別符號,那麼您的程式碼將無法在 JDK 11 及更高版本中編譯。它會在 JDK 8 中生成警告,並從 JDK 9 開始生成錯誤。

舉個例子:

static Object _ = new Object();

此程式碼從編譯器生成以下錯誤訊息:

MyClass。java:2: error: as of release 9, ‘_’ is a keyword, and may not be used as a legal identifier。

如果您將-sourceand-target選項與 一起使用javac,請檢查您使用的值。

支援的-source/-target 值為 17(預設值)、16、15、14、13、12、11、10、9、8 和 7。

在 JDK 8 中,不推薦設定 -source和-target的值為1。5/5或更早的值,如果設定這樣值有警告。在 JDK 9 及更高版本中,這些值會直接丟擲Error。

>javac -source 5 -target 5 Sample。java warning: [options] bootstrap class path not set in conjunction with -source 5 error: Source option 5 is no longer supported。 Use 6 or later。 error: Target option 1。5 is no longer supported。 Use 1。6 or later。

使用新的——release標誌而不是-source -target選項。請參閱

Java Development Kit Tool Specifications

中的javac。

—release標誌的有效值和-source -target選項相同。

javac命令可以識別和處理大於等於 JDK 1。0。2 版本原始檔。

請參閱JEP 182:停用 javac -source 和 -target 選項的策略。

JDK的內部API,例如

sun。misc。Unsafe

在 JDK 11 及更高版本中仍然可以訪問,但大多數 JDK 的內部 API 在編譯時無法訪問。您可能會收到編譯錯誤,表明您的應用程式或其庫依賴於內部 API。

識別依賴關係,請執行 Java 依賴關係分析工具。如果可能,請更新您的程式碼以使用支援的替換 API。

您可以使用——add-exports和——add-opens 選項作為臨時解決方法來編譯引用 JDK 內部類的原始碼。有關這些選項的更多資訊, 請參閱JEP 261:JDK 中的模組系統和強封裝。

您可能會看到比以前更多的棄用警告。

在您的程式碼上執行 jdeps

在您的應用程式上執行該jdeps工具以檢視您的應用程式和庫所依賴的包和類。如果您使用內部 API,則jdeps可能會建議替換以幫助您更新程式碼。

要查詢對內部 JDK API 的依賴關係,請jdeps使用該-jdkinternals選項執行。例如,在呼叫了sun。misc。BASE64Encoder的類上執行jdeps,您將看到:

>jdeps -jdkinternals Sample。class Sample。class -> JDK removed internal API Sample -> sun。misc。BASE64Encoder JDK internal API (JDK removed internal API) Warning: JDK internal APIs are unsupported and private to JDK implementation that are subject to be removed or changed incompatibly and could break your application。 Please modify your code to eliminate dependency on any JDK internal APIs。 For the most recent update on JDK internal API replacements, please check: https://wiki。openjdk。java。net/display/JDK8/Java+Dependency+Analysis+Tool JDK Internal API Suggested Replacement ———————— ——————————- sun。misc。BASE64Encoder Use java。util。Base64 @since 1。8

如果您使用 Maven,則有一個jdeps 可用的外掛。

有關jdeps語法,請參閱jdepsJava

開發工具包工具規範

但是注意:jdeps是靜態分析工具,程式碼的靜態分析可能無法提供完整的依賴關係列表。比如如果程式碼使用反射來呼叫內部 API,則jdeps無法發出警告。

遷移

JDK 8 和更高版本的 JDK 之間發生了重大變化。

每個新的 Java SE 版本都會引入一些與以前版本的二進位制、原始碼和行為不相容的問題。JDK 9 及之後的 Java SE 平臺的模組化帶來了許多好處,但也帶來了許多變化。僅使用官方 Java SE 平臺 API 和受支援的特定於 JDK 的 API(supported JDK-specific APIs) 的程式碼應繼續工作而無需更改。使用 JDK 內部 API 的程式碼應繼續執行,但應遷移以使用受支援的 API。

某些 API 在其預設行為中已被設定為不可訪問、刪除或更改。編譯或執行應用程式時可能會遇到問題。請參閱 已刪除的工具和元件以及安全更新。

以下部分描述了將 JDK 8 應用程式遷移到更高版本的 JDK 時應注意的 JDK 包中的更改。

檢視執行應用程式時可能遇到的更改列表。

JDK中的強封裝

一些工具和庫使用反射來訪問僅供內部使用的 JDK 部分。這種反射的使用會對 JDK 的安全性和可維護性產生負面影響。為了幫助遷移,JDK 9 到 JDK 16 允許這種反射繼續進行,但發出有關

非法反射訪問

的警告。但是,JDK 17 是

強封裝(strongly encapsulated)

的,所以預設情況下不再允許這種反射。訪問 API 的非public欄位和非public方法的程式碼java。*將丟擲 InaccessibleObjectException。

請注意,所有 JDK 版本(包括 JDK 17)中的工具和庫都可以使用sun。misc和sun。reflect 包進行反射。

java啟動選項——illegal-access 允許在 JDK 9 到 JDK 16 中使用反射呼叫JDK 內部Api。您可以指定以下引數:

——illegal-access=permit: 允許類路徑上的程式碼反映java。*JDK 8 中存在的包的內部結構。對任何此類元素的第一次反射訪問操作會導致發出警告,但在此之後不會發出警告。

——illegal-access=warn:導致為每個非法反射訪問操作發出警告訊息。

——illegal-access=debug: 導致為每個非法反射訪問操作顯示警告訊息和堆疊跟蹤。

——illegal-access=deny: 禁用所有非法反射訪問操作,但由其他命令列選項啟用的操作除外,例如 ——add-opens。

許多工具和庫已更新以避免依賴 JDK 內部Api,而是使用在 JDK 8 和 17 之間引入的標準 Java API。同時——illegal-access啟動選項在 JDK 17 中已過時。在 JDK 17 中使用此啟動器選項,無論是使用permit, warn,debug或deny, 除了發出警告訊息外沒有任何作用。

如果您無法獲取或部署較新版本的工具和庫,則有兩個命令列選項可讓您授予對舊版本工具和庫的特定內部 API 的訪問許可權:

——add-exports:如果您有一個較舊的工具或庫需要使用已被強封裝的內部 API,則使用 ——add-exports執行時選項。您還可以 ——add-exports在編譯時使用來訪問內部 API。

——add-opensjava。*:如果您有一個較舊的工具或庫需要透過反射訪問 API 的非public欄位和非public方法,請使用該——add-opens選項。

請參閱JEP 403:預設情況下強封裝 JDK 內部。

——add-exports

如果您有一個較舊的工具或庫需要使用已被強封裝的內部 API,請使用 ——add-exports執行時選項。您還可以——add-exports在編譯時使用來訪問內部 API。

該——add-exports選項的語法是:

——add-exports /=(,)*

其中

是模組名稱,

是包的名稱。

——add-exports如果目標模組讀取源模組, 該選項允許目標模組中的程式碼訪問源模組的命名包中的型別。

作為一種特殊情況,如果

是 ALL-UNNAMED,則源包將匯出到所有未命名的模組,無論它們最初存在還是稍後建立。例如:

——add-exports java。management/sun。management=ALL-UNNAMED

此示例允許所有未命名模組中的程式碼(類路徑上的程式碼)訪問 java。management/sun。management。

如果類路徑上的程式碼使用反射 API (

setAccessible(true)

) 嘗試訪問java。*API 的非公共欄位和方法,則程式碼將失敗。預設情況下,JDK 17 不允許這樣做。但是,您可以使用該 ——add-opens選項來允許這樣做。有關更多資訊,請參見——add-opens部分。

如果在類路徑上執行的應用程式oldApp必須使用模組的未匯出com。sun。jmx。remote。internal包java。management,則可以透過以下方式授予它所需的訪問許可權:

——add-exports java。management/com。sun。jmx。remote。internal=ALL-UNNAMED

您還可以使用Add-ExportsJAR 檔案清單屬性:

Add-Exports:java。management/sun。management

謹慎使用該——add-exports選項。您可以使用它來訪問庫模組甚至 JDK 本身的內部 API,但這樣做的風險由您自己承擔。如果該內部 API 更改或被刪除,那麼您的庫或應用程式將失敗。

參見JEP 261:模組系統。

——add-opens

一些工具和庫使用反射 API (

setAccessible(true)

) 嘗試訪問java。*API 的非公共欄位和方法。預設情況下,這在 JDK 17 上不再可能,但您可以使用——add-opens命令列上的選項為特定工具和庫啟用它。

——add-opens語法如下:

——add-opens /=(,)*

無論模組宣告如何, 此選項都允許

開啟

作為一種特殊情況,如果

是ALL-UNNAMED,則源包將匯出到所有未命名的模組,無論它們最初存在還是稍後建立。例如:

——add-opens java。management/sun。management=ALL-UNNAMED

此示例允許類路徑上的所有程式碼訪問java。management/sun。management包中公共型別的非公共成員。

新版本字串方案

JDK 10 對 JDK 9 中引入的版本字串方案引入了一些小的更改,以更好地適應基於時間的釋出模型。JDK 11 及更高版本保留了 JDK 10 中引入的版本字串格式。

如果您的程式碼依賴於版本字串格式來區分主要、次要、安全和補丁更新版本,那麼您可能需要更新它。

新版本字串的格式為:

$FEATURE。$INTERIM。$UPDATE。$PATCH

添加了用於解析、驗證和比較版本字串的簡單 Java API。看java。lang。Runtime。Version。

請參閱Java 平臺中的版本字串格式

,標準版安裝指南

有關 JDK 9 中引入的版本字串的更改,請參閱 JEP 223:新版本字串方案。

有關 JDK 10 中引入的版本字串更改,請參閱JEP 322: Time-Based Release Versioning。

已安裝的 JDK/JRE 映像的更改

對 JDK 和 JRE 進行了重大更改。

更改了 JDK 和 JRE 佈局

安裝 JDK 後,如果檢視檔案系統,您會注意到目錄佈局與 JDK 9 之前的版本不同。

JDK 11 及更高版本

JDK 11 及更高版本沒有 JRE 映像。請參閱Java 平臺中 JDK的已安裝目錄結構

,標準版安裝指南

DK 9 和 JDK 10

以前的版本有兩種型別的執行時映像:JRE,它是 Java SE 平臺的完整實現,以及 JDK,它將整個 JRE 包含在一個jre/目錄中,以及開發工具和庫。

在 JDK 9 和 JDK 10 中,JDK 和 JRE 是兩種型別的模組化執行時映像,包含以下目錄:

bin

: 包含二進位制可執行檔案。

conf

: 包含。properties、。policy和其他型別的檔案,供開發人員、部署人員和終端使用者編輯。這些檔案以前在lib目錄或其子目錄中找到。

lib

: 包含動態連結的庫和 JDK 的完整內部實現。

在 JDK 9 和 JDK 10 中,仍然有單獨的 JDK 和 JRE 下載,但每個都有相同的目錄結構。JDK 映像包含歷史上在 JDK 中發現的額外工具和庫。沒有jdk/與jre/包裝器目錄,並且二進位制檔案(例如java命令)不重複。

請參閱JEP 220:模組化執行時映像。

新的類載入器實現

JDK 9 和更高版本維護了自 1。2 版本以來存在的類載入器的層次結構。但是,為了實現模組系統,進行了以下更改:

應用程式類載入器不再是URLClassLoader而是一個內部類。它是既不是 Java SE 也不是 JDK 模組的模組中的類的預設載入器。

擴充套件類載入器已重新命名;它現在是平臺類載入器。Java SE 平臺中的所有類都保證透過平臺類載入器可見。

僅僅因為一個類透過平臺類載入器可見並不意味著該類實際上是由平臺類載入器定義的。Java SE 平臺中的一些類是由平臺類載入器定義的,而其他類是由引導類載入器定義的。應用程式不應依賴於哪個類載入器定義了哪個平臺類。

在 JDK 9 中實現的更改可能會影響建立類載入null器(即引導類載入器)作為父類載入器並假定所有平臺類對父類可見的程式碼。可能需要更改此類程式碼以使用平臺類載入器作為父級(請參閱 ClassLoader。getPlatformClassLoader)。

平臺類載入器不是URLClassLoader,而是一個內部類。

引導類載入器仍然內建在 Java 虛擬機器null中,由ClassLoaderAPI。它定義了一些關鍵模組中的類,例如java。base。 因此,它定義的類遠少於 JDK 8 中的類,因此部署-Xbootclasspath/a或建立類載入null器作為父級的應用程式可能需要如前所述進行更改。

刪除了 rt。jar 和 tools。jar

以前儲存在lib/rt。jar、lib/tools。jar中的類和資原始檔lib/dt。jar以及各種其他內部 JAR 檔案以更有效的格式儲存在lib目錄中特定於實現的檔案中。

刪除rt。jar和類似檔案會導致以下方面的問題:

從 JDK 9 開始,ClassLoader。getSystemResource不返回指向 JAR 檔案的 URL(因為沒有 JAR 檔案)。相反,它返回一個 jrtURL,該 URL 命名儲存在執行時映像中的模組、類和資源,而不顯示映像的內部結構或格式。

例如:

ClassLoader。getSystemResource(“java/lang/Class。class”);

在 JDK 8 上執行時,此方法返回以下形式的 JAR URL:

jar:file:/usr/local/jdk8/jre/lib/rt。jar!/java/lang/Class。class

模組化的映象(modular image)不包含任何 JAR 檔案,因此這種形式的 URL 沒有意義。在 JDK 9 及更高版本上,此方法返回:

jrt:/java。base/java/lang/Class。class

java。security。CodeSource

API 和安全策略檔案使用 URL 來命名要被授予特定許可權的程式碼庫的位置。請參閱

Java 平臺

中的策略檔案語法,標準版安全開發人員指南。執行時元件需要的特定許可權當前使用檔案URL的方式定義在conf/security/java。policy檔案中(Components of the runtime system that require specific permissions are currently identified in the conf/security/java。policy file by using file URLs) 。

舊版本的 IDE 和其他開發工具需要能夠列舉儲存在執行時映像中的類和資原始檔,並透過開啟和讀取rt。jar類似檔案來直接讀取它們的內容。這對於模組化映象是不可能的。

移除了擴充套件機制

在 JDK 8 及更早版本中,擴充套件機制使執行時環境可以查詢和載入擴充套件類,而無需在類路徑上專門命名它們。從 JDK 9 開始,如果您需要使用擴充套件類,請確保 JAR 檔案位於類路徑上。

在 JDK 9 和 JDK 10 中,如果設定了系統屬性或目錄存在,javac編譯器和java 啟動器將退出。要另外檢查特定於平臺的系統範圍目錄,請指定 命令列選項。如果目錄存在且不為空,這將導致發生相同的退出行為。擴充套件類載入器保留在 JDK 9(及更高版本)中,並被指定為平臺類載入器(參見java。ext。dirslib/ext-XX:+CheckEndorsedAndExtDirsgetPlatformClassLoader。) 但是,在 JDK 11 中,此選項已過時,使用時會發出警告。

以下錯誤表示您的系統配置為使用擴充套件機制:

/lib/ext exists, extensions mechanism no longer supported; Use -classpath instead。 。Error: Could not create the Java Virtual Machine。 Error: A fatal exception has occurred。 Program will exit。

如果設定了java。ext。dirs系統屬性, 您將看到類似的錯誤。

要修復此錯誤,請刪除ext/目錄或java。ext。dirs系統屬性。

請參閱JEP 220:模組化執行時映像。

刪除了經認可的標準覆蓋機制

java。endorsed。dirs系統屬性和lib/endorsed目錄將不再存在。如果檢測到任何一個,編譯器和啟動器將退出javac。java

從 JDK 9 開始,您可以使用可升級模組或將 JAR 檔案放在類路徑中實現類似功能。

此機制旨在讓應用程式伺服器覆蓋 JDK 中使用的元件。要更新的包將被放入 JAR 檔案中,系統屬性java。endorsed。dirs會告訴 Java 執行時環境在哪裡可以找到它們。如果未指定此屬性的值,則使用預設值$JAVA_HOME/lib/endorsed。

在 JDK 8 中,您可以使用-XX:+CheckEndorsedAndExtDirs命令列引數來檢查系統上任何位置的此類目錄。

在 JDK 9 及更高版本中,如果設定了java。endorsed。dirs系統屬性或lib/endorsed目錄存在 ,javac編譯器和java啟動器將退出。

以下錯誤意味著您的系統配置為使用認可的標準覆蓋機制:

/lib/endorsed is not supported。 Endorsed standards and standalone APIs in modular form will be supported via the concept of upgradeable modules。 Error: Could not create the Java Virtual Machine。 Error: A fatal exception has occurred。 Program will exit。

如果設定了java。endorsed。dirs系統屬性, 您將看到類似的錯誤。

要修復此錯誤,請刪除lib/endorsed目錄或取消設定java。endorsed。dirs系統屬性。

請參閱JEP 220:模組化執行時映像。

刪除了 macOS 特定的功能

從 JDK 9 開始刪除了macOS 特定功能。

特定於平臺的桌面功能

該類包含對 Apple 特定的和包java。awt。Desktop中 API 的替換。新的 API 取代了 macOS API,並且獨立於具體作業系統。 com。apple。eawtcom。apple。eio

com。apple。eawt和包中的 APIcom。apple。eio是封裝的,因此您將無法在 JDK 9 或更高版本中針對它們進行編譯。但是,它們在執行時仍可訪問,因此編譯為舊版本的現有程式碼繼續執行。apple最終,使用andcom。apple 包及其子包 中的內部類的庫或應用程式 將需要遷移到新的 API。

com。apple。concurrent 和包被刪除,apple。applescript沒有任何替換。

請參閱JEP 272:特定於平臺的桌面功能。

移除 AppleScript 引擎

AppleScript 引擎,特定於平臺的javax。script實現,已在 JDK 中刪除,沒有任何替換。

AppleScript 引擎在最近的版本中幾乎無法使用。該功能僅在 JDK 7 或 JDK 8 中有效,系統上已經有 Apple 版本的AppleScriptEngine。jar檔案。

Windows 登錄檔項更改

Java 11 及更高版本的安裝程式會在安裝 JDK 時建立 Windows 登錄檔項。對於 JDK 16,安裝程式會建立以下 Windows 登錄檔項:

“HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK”

“HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\16”

如果安裝了兩個版本的 JDK,則會建立兩個不同的 Windows 登錄檔項。例如,如果 JDK 15。0。1 與 JDK 16 一起安裝,則安裝程式會建立另一個 Windows 登錄檔項,如下所示:

“HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK”

“HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\16”

“HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\15。0。1”

部署

Java 部署技術在 JDK 9 中被棄用,並在 JDK 11 中被刪除。

Java Applet 和 WebStart 功能,包括 Applet API、Java 外掛、Java Applet 檢視器、JNLP 和 Java Web Start(包括 javaws 工具)在 JDK 9 中均已棄用

使用jlinkJDK 9 引入的工具來打包和部署專用執行時,而不是依賴於預安裝的系統 JRE。

刪除了啟動時 JRE 版本選擇功能

從 JDK 9 開始,刪除了請求不是在啟動時啟動的 JRE 的 JRE 版本的能力。

現代應用程式通常使用 Java Web Start (JNLP)、本機 OS 打包系統或活動安裝程式進行部署。這些技術有自己的方法來管理所需的 JRE,方法是根據需要查詢或下載和更新所需的 JRE。這使得啟動器的啟動時可以選擇已過時JRE 版本。

在以前的版本中,您可以指定啟動應用程式時要使用的 JRE 版本(或版本範圍)。可以透過命令列選項和應用程式 JAR 檔案中的清單條目來選擇版本。

從 JDK 9 開始,java launcher 修改如下:

-version:如果在命令列上給出 了選項,則發出錯誤訊息並退出。

JRE-Version如果在 JAR 檔案中找到清單條目 ,則發出警告訊息並繼續。

請參閱JEP 231:刪除啟動時 JRE 版本選擇。

垃圾收集的變化

將 G1 設為預設垃圾收集器

刪除了部分GC 選項

以下 GC 組合將導致您的應用程式在 JDK 9 及更高版本中無法啟動:

DefNew + CMS

ParNew + SerialOld

Incremental CMS

CMS 的前臺模式也已被刪除。刪除的命令列標誌是-Xincgc、-XX:+CMSIncrementalMode、 -XX:+UseCMSCompactAtFullCollection、-XX:+CMSFullGCsBeforeCompaction和 -XX:+UseCMSCollectionPassing。

命令列標誌-XX:+UseParNewGC不再有效。該ParNew標誌只能用於 CMS 和 CMS 需要ParNew。因此,該-XX:+UseParNewGC標誌已被棄用,並且在未來的版本中刪除。

請參閱JEP 214:刪除 JDK 8 中已棄用的 GC 組合。

刪除了永久代

JDK 8 中刪除了永久代,相關的 VM 選項會導致列印警告。您應該從指令碼中刪除這些選項:

-XX:MaxPermSize=

size

-XX:PermSize=

size

在 JDK 9 及更高版本中,JVM 會顯示如下警告:

Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option MaxPermSize; support was removed in 8。0

使用了永久代引數的工具可能必須更新。

請參閱JEP 122:刪除永久代和JDK 9 發行說明 - 刪除的 API、功能和選項。

GC 日誌輸出的更改

垃圾收集(GC)日誌使用JVM統一的日誌框架,新舊日誌存在一些差異。您正在使用的任何 GC 日誌解析器都可能需要更改。

您可能還需要更新 JVM 日誌記錄選項。所有與 GC 相關的日誌記錄都應使用 gc標籤(例如—Xlog:gc),通常與其他標籤結合使用。和選項已被棄用 —XX:+PrintGCDetails。-XX:+PrintGC

請參閱

Java Development Kit Tool Specifications

和JEP 271: Unified GC Logging中的使用 JVM Unified Logging Framework 啟用日誌記錄。

正則表示式匹配中的行為變化

java。util。regex。Pattern用方括號定義正則表示式中的字元類。例如,[abc] 匹配a,b,或c。否定字元類是用緊跟在左大括號後面的插入符號定義的。例如, [^abc]匹配除a,b, 或之外的任何字元c。

在 JDK 8 及更早版本中,否定字元類不會否定巢狀字元類。例如,[^a-b[c-d]e-f]匹配 c但不匹配a,或者 e因為它們不在巢狀類中。運算子一個接一個地應用。^在此示例中,在巢狀之前應用了否定運算子。在 JDK 8 及更早的版本中,運算子^僅應用於字元類中的最外層字元,

而不應用於

到巢狀的字元類。這種行為令人困惑且難以理解。

但是,在 JDK 9 及更高版本中,否定運算子應用於所有巢狀字元類。例如, [^a-b[c-d]e-f]不匹配 c。

為了進一步解釋,請考慮以下正則表示式:

[^a-d&&c-f]

在 JDK 8 中,^首先應用運算子,因此該示例被解釋為 [^a-d] 與[c-f]。 這匹配 e and f但不 匹配a, b, c, or d。

在 JDK 9 及更高版本中,&&首先應用運算子,因此該示例被解釋為[a-d]&&[c-f]。 這匹配 a, b,e和f但不匹配c or d。

作為最佳實踐,尋找使用字元類的正則表示式以及否定、交集和巢狀類的某種組合。可能需要調整這些正則表示式以考慮更改的行為。

後續步驟

如果需要,使用javac工具 -–release中的新標誌交叉編譯到平臺的舊版本 。

利用 IDE 的建議來使用最新功能更新程式碼。

透過執行靜態分析工具jdeprscan檢視您的程式碼是否使用了已棄用的 API 。正如本指南中已經提到的,可以從 JDK 中刪除 API,但必須提前通知。

熟悉多版本 JAR 檔案等新功能(請參閱jar)。

實戰

目標

使用JDK 17 構建hutool,新建springboot工程並呼叫JDK 17呼叫hutool中的功能。

步驟

先用JDK 17直接構建,修改pom。xml,將JDK版本修改為17

首先確保已經安裝配置好JDK 17:這裡使用了更高版本的19

JDK 8升級高版本JDK指南

JDK 8升級高版本JDK指南

執行mvn clean compile進行編譯

JDK 8升級高版本JDK指南

編譯失敗,因為javax。xml包已經被移除。

在hutool-core/pom。xml中新增javax。xml的依賴

javax。xml。bind jaxb-api 2。3。0 com。sun。xml。bind jaxb-core 2。3。0 com。sun。xml。bind jaxb-impl 2。3。0

重新編譯,全部編譯成功

JDK 8升級高版本JDK指南

執行mvn install -Dmaven。javadoc。skip=true 執行單元測試並本地安裝hutool,執行單元測試在這裡很重要,因為不能保證升級JDK之後,程式碼邏輯不受影響。

JDK 8升級高版本JDK指南

對於Unable to make protected native java。lang。Object java。lang。Object。clone() throws java。lang。CloneNotSupportedException accessible: module java。base does not “opens java。lang” to unnamed module @5a65309b 錯誤,明顯是因為Java 9開始的模組系統強封裝造成的,我們需要新增——add-exports。

org。apache。maven。plugins maven-surefire-plugin ——add-opens java。base/java。lang=ALL-UNNAMED

新增上面外掛和配置之後,java。lang。CloneNotSupportedException不再出現,現在來處理java。lang。OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached。這個錯誤是因為要建立的執行緒數超過了系統的限制,調大即可。

解決這個錯誤之後,出現module java。base does not “opens java。util。regex” to unnamed module @15615099

JDK 8升級高版本JDK指南

繼續新增opens

JDK 8升級高版本JDK指南

出現新的錯誤,這是因為JDK移除了javax。activation,新增依賴就好

JDK 8升級高版本JDK指南

新增如下依賴

JDK 8升級高版本JDK指南

出現新的錯誤java。lang。NoSuchFieldException: modifiers

JDK 8升級高版本JDK指南

因為JDK 12開始,field欄位不再有modifiers,所以更改程式碼以滿足要求

JDK 8升級高版本JDK指南

再次重新構建,出現新的錯誤

JDK 8升級高版本JDK指南

新增opens:

JDK 8升級高版本JDK指南

再次構建,出現如下錯誤

JDK 8升級高版本JDK指南

debug之後發現,這個錯誤是因為高版本jdk使用MemberName來判斷field的modifier,讓前面程式碼中修改失敗,所以只能先暫時跳過這個測試並不使用這個功能,等待專案組更新版本支援高版本JDK。

JDK 8升級高版本JDK指南

至此構建全部成功

JDK 8升級高版本JDK指南

新建SpringBoot工程

JDK 8升級高版本JDK指南

引入之前構建在本地的hutool

JDK 8升級高版本JDK指南

編寫一個簡單的測試類:

@RestController public class TestController { @GetMapping(“/test/{plainText}”) public ResponseEntity test(@PathVariable(“plainText”) String plainText) { int hash = HashUtil。apHash(plainText); return ResponseEntity。ok(hash); } }

啟動應用進行測試:

JDK 8升級高版本JDK指南