如何從 Java 8 升級到 Java 12,升級收益及問題處理技巧


如何從 Java 8 升級到 Java 12,升級收益及問題處理技巧

核心要點

  • 從 Java 8 之後,Java 引入了很多有用的新語言特性,以及新工具和性能改善(尤其是垃圾收集相關的優化)。
  • 在選擇升級時,我們所面臨的選擇是升級到最新的 Java(12)並準備每六個月升級一次,還是升級到最新的 LTS(11)版本,這樣能夠給自己三年的時間再去考慮下一次升級;
  • 不要忽略編譯器警告。在現代 Java 領域中,廢棄的功能要被更嚴肅地對待,Java 10 和 Java 11 都刪除了 API;
  • Java 9 所帶來的一個變化就是內部 API(大多數是 sun.misc.* 打頭的包中的類)被隱藏了,導致無法使用了。在 Java 11 中,非核心 JDK 的 API 也被刪除了。這些變化可能會影響到你的應用,但是有明確的遷移路徑來避免這些問題;
  • 在度過了第一次升級的艱難時期之後,我們至少可以每六個月在最新版本的 Java 上測試一下我們的應用,比如在 CI 環境中。

一般來講,企業級應用不願意升級到最新版本的 Java,除非該版本已經得到了充分的證明。Java 9 在 2017 年 9 月發佈之後,我們 每六個月就會有一個新版本的 Java 發佈這一切變得更具挑戰性。所以,儘管 Java 9 發佈還不到兩年的時間,最新的版本已經是 Java 12 了。

這種變化節奏可能會令人望而生畏,而且 從目前主流應用程序所運行的 Java 8升級到 Java 12 可能會有很多工作要做。在本文中,我們將會看一下:

  • 升級所帶來的收益;
  • 升級過程中可能會出現的問題;
  • 關於升級的一些技巧。

為何要升級?

在深入研究如何升級的細節之前,我們應該認真考慮一下為何要升級。

特性 作為開發人員,我們最關心的是語言的每次升級所帶來的新特性和 API。從 Java 8 之後,我們已經有了很多的新特性,並且還有改變我們工作方式的新工具。在這裡我挑選了一些開發人員認為最新版本的 Java 讓他們的工作更容易的新特性。

局部變量類型推斷(Local-variable type inference)(即 var )是語法糖的一個絕佳示例,它有助於減少樣板式代碼。它並不是針對所有可讀性問題的解決方案,但是在正確的地方使用它能夠幫助代碼的讀者關注業務邏輯(它要做什麼)而不是樣板式的代碼(它是如何做的)。

針對集合的便利的工廠方法(Convenience Factory Methods for Collections)能夠大幅度簡化集合的創建,比如 List、Map 和 Set。這些工廠方法還能創建不可修改的集合,讓它們使用起來更加安全。

收集至不可變的集合,這個特性能夠針對 Streams 操作使用新的 Collector,它會將結果放到一個不可變的集合中。

Predicate::not提供了一個很簡便的方式來否定斷言 lambdas 表達式或方法引用,同樣能夠減少我們的樣板式代碼。

Optional 上的新方法能夠讓我們在使用 Optional 來替換繁瑣的 if 語句時,有了更多的函數式編程可選方案。

JShell是一個 REPL,它能夠讓我們運行 Java 編寫的代碼行,甚至腳本。它是一種體驗新特性的好辦法,我們可以在本地開發環境中使用它,避免在生產環境的應用代碼中採用新版本的 Java

HttpClient構建到了 JDK 中。幾乎所有人都在以某種形式使用 HTTP,要麼通過 Web 應用,要麼通過 REST 服務或其他方式。內置的客戶端移除了對外部的依賴,同時支持 HTTP 1.1 和 2 的同步和異步編程模型。

JAR 文件的多發佈版本(Multi release jar files) ,這是一個工具庫,開發人員可以使用它來支持需要最新版本的 Java,同時還要支持使用舊版本的場景。

JLink是一個非常棒的工具,由 Java 模塊系統(Java Module System)提供,它能夠讓我們只打包和部署 JDK 中我們真正需要的部分。

性能

通常來講,新版本都要比之前的版本表現更好。“更好”會有多種表現形式,但是在最近的釋放版本中,我們看到了啟動時間的改善、內存消耗的減少,以及使用特定的 CPU 指令從而讓代碼使用更少的 CPU 週期。Java 9、10、11 和 12 都對垃圾收集功能進行了重大的變更和改進,包括[將默認的垃圾收集器改為 G1) 、對 G1 的改進以及三個實驗性的垃圾收集器(Java 11 中的 Epsilon和ZGC ,Java 12 中的 Shenandoah)。三個收集器看起來可能有些過分了,但是每個收集器都針對不同的使用場景進行了優化,所以現在你可以從中選擇一個最適合你的應用程序的現代垃圾收集器。

成本的削減

Java 最近版本的改善可能會為你帶來成本的削減。尤其是像 JLink 這樣的工具能夠減少部署製件的大小,再加上內存使用方面的改善,這都會降低雲計算的成本。

另外還有一項潛在的收益,那就是使用現代版本的語言會吸引更多的開發人員,因為很多開發人員都希望有機會學習新的東西並更新自己的技能。使用現代版本的 Java 有利於開發人員的留任和招聘,最終會影響團隊的運行成本。

升級的注意事項

從 Java 8 以來,很多事情都發生了變化,而且不僅侷限於語言級別的特性。甲骨文公司開始發佈兩個不同的構建版本,它們有著不同的許可證(其中一個是商業構建版本,在生產環境使用的話需要付費,另外一個是開源的 OpenJDK 構建版本),並且更改了他們的升級和支持模型。這在社區引發了很多的爭論,但是最終 在選擇使用哪個 JDK 方面,我們會有更多的選擇。我們必須要考慮採用什麼方案以及要從 JDK 中得到什麼。

該選擇哪個版本?

鑑於最新的版本已經是 Java 12 了,在從 Java 8 進行升級時,我們似乎有很大的可選餘地。實際上,這個選擇要簡單得多。現在,我們每六個月就會有一個發佈版本,每個新的發佈版本都會取代之前的版本。唯一例外的是每三年會有一個長期支持(Long Term Support,LTS)的發佈版本。這樣的話,就允許組織選擇最合適的升級路徑,要麼每六個月就升級到最新的版本,要麼採用傳統方式每三年進行一次大的升級,也就是升級到 LTS 版本。Java 8 是 LTS 版本,但是甲骨文在今年一月已經停止了在商業應用中對它的(免費)更新。Java 11 是目前的 LTS 版本,儘管甲骨文目前還沒有為其提供免費的商業更新就發佈了 Java 12,但是有很多組織都提供了大量的 JDK 構建版本,這些組織將會提供至少三年的更新。

你所面臨的選擇就是,要麼升級到最新版本的 Java(12)並且為每六個月一次的更新做好準備,要麼升級到最新的 LTS 版本(11),這樣的話,你會有三年的時間考慮下一次的升級。我們建議更大的組織採用的策略是從 LTS 版本升級到下一個 LTS 版本,而創業型的小型組織可以每六個月升級一次。更快速且可預測的發佈節奏所帶來的好處是它能夠同時支持這兩種方案。

使用哪個構建版本?

我們不能僅僅認為甲骨文沒有為 LTS 版本提供免費更新,就認為無法在生產環境使用 LTS 版本並獲取免費更新。現在,有很多的組織和廠商都在提供 OpenJDK (JDK 的參考實現,甲骨文的商用 JDK 甚至都是基於它構建的)的構建版本。我在這裡不會對它們一一列舉,請參閱 這個列表 ,它涵蓋了免費 OpenJDK 構建版本的提供商、免費公開更新可以維護到什麼時間以及商業支持的詳細信息。

看上去,這比以往要複雜得多。當然,還有很多其他的因素要考慮,這是更多選擇所帶來的副作用。Java Champions 正在就許可證、支持、更新以及不同方案的話題準備一個更加完整的討論,在 [QCon London 2019 上也有關於該話題的問答環節

Java 9 會破壞一切嗎?

很多開發人員在從 Java 8 進行升級時,最主要的顧慮在於 Java 9 所帶來的重大變化,他們害怕這些變更會破壞掉應用程序。其中一項變更就是 對內部 API 的封裝,這意味著 JDK 中一些過去可用的方法無法使用了。在 Java 9 發佈的時候,這一點,再加上對 tools.jar 和 rt.jar 的移除 導致很多人感到非常擔心,但事後證明,相對於應用開發人員,它們對庫、框架和語言開發人員所帶來的問題更嚴重。

如果你的應用沒有做什麼不應該做的事情(比如使用內部 API 或者已廢棄的方法),遷移至 Java 9 或更高版本並不像看上去那麼恐怖。社區面臨的很多問題實際上已經通過我們所使用的構建工具和庫解決掉了。

升級指南

每個升級過程都是與要遷移的應用相關的。但是,有一些基本的良好實踐能夠幫助我們簡化過程。我們按照要處理的順序將它們列到了這裡,你會發現最初的一些步驟根本就不需要升級版本的 JDK。

處理編譯告警

警告的出現都是有原因的,如果你遇到警告的話,它們通常預示著一些將來會消失掉的特性。如果你在 Java 8 JDK 上使用 Java 8 的話,那麼可能會看到廢棄提醒的警告,或者某些不該使用的特性的警告(參見圖 1),在嘗試升級到更新版本的 JDK 之前,請解決這些警告。

如何從 Java 8 升級到 Java 12,升級收益及問題處理技巧


圖 1:來自 JDK 8 的編譯器告警示例


注意,在現代 Java 中,廢棄的功能要被更嚴肅地對待, Java 10和 Java 11都刪除了 API。

檢查對內部 API 的使用

Java 9 的一個變化就是內部 API(大多數都是以 sun.misc.* 開頭的類)隱藏了起來,無法繼續使用了。

JDK 中有一個名為 jdeps的工具,你可以使用 -jdkinternals標記來運行它,這樣的話能夠檢查一組類是否用到了它不該使用的內容。舉例來說,如果我在項目的輸出中運行如下的命令:

<code>> $JAVA_HOME\\bin\\jdeps-jdkinternals.
/<code>

我會得到如下的輸出:

<code>. -> /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/rt.jar
com.mechanitis.demo.sense.twitter.connector.TwitterOAuth(.)
-> sun.misc.BASE64EncoderJDK internal API (rt.jar)
-> sun.misc.UnsafeJDK internal API (rt.jar)

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 yourcodeto 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.BASE64EncoderUse [email protected]

sun.misc.UnsafeSee http://openjdk.java.net/jeps/260
/<code>

這個工具不僅能夠識別出使用了內部 API 的類,還提供了該使用什麼替代方案的建議。

升級構建工具

如果你使用 Maven 或 Gradle 的話,也需要進行升級。

Gradle

Gradle 5.0 引入了對 Java 11 的支持,所以我們至少要使用 5.0 版本。 當前的最新版本是 5.4.1 ,它提供了對 Java 12 的支持。

Maven

我們至少要使用 3.5.0( 最新的版本是 3.6.1 ),需要確保 Maven 編譯器插件至少是 3.8 版本:

<code><plugin>
<groupid>org.apache.maven.plugins/<groupid>
<artifactid>maven-compiler-plugin/<artifactid>
<version>3.8.0/<version>
<configuration>
<release>11/<release>
/<configuration>
/<plugin>
/<code>

更新依賴

關於遷移至 Java 9 或更高版本你所聽說的一些問題,都是庫和框架需要面對的(而且可能已經被它們所修復了)。例如,很多框架在幕後都會使用反射和內部 API。為了保證你的應用能夠繼續正常運行,你需要確保所有的依賴項都是最新的。很多庫都進行了更新,以便於支持 Java 9 和更高版本, 社區正在努力確保 這個過程能夠持續下去。

有些依賴項可能需要替換。舉例來說,很多庫都已經使用 Byte Buddy 來實現代碼生成和代理,因為它可以與所有現代版本的 Java 兼容。在研究遷移至 Java 11 的時候,你必須要深入理解你的依賴關係,並搞清楚它們是否已經進行了更新以支持 Java 8 以後的版本。

添加對已移除功能的依賴

非 JDK 核心的 API 已經被移除掉了,這包括 Java EE 和 Corba 模塊 以及JavaFX 。這個問題解決起來通常很簡單,只需要在你的依賴管理中添加正確的庫就可以了。例如,在 Gradle 或 Maven 中添加對 JAXB 的依賴。JavaFX 稍微複雜一些,但是 在 OpenJFX 站點上有非常棒的文檔 。

使用新 JDK 運行應用

要使用最新版本的 Java,你並不需要重新編譯,這就是語言開發人員如此努力保持向後兼容性的原因之一。你可以無需任何代碼變更就可以在持續集成環境(舉例來說)中使用新的 JDK 來運行你的應用。

使用新的 JDK 進行編譯

在前面的步驟中,我們依然可以使用 Java 8 編譯應用。只有在完成了這些步驟之後,你才應該考慮針對 Java 11 或 12 進行編譯。請記住,如果不想使用新特性的話,我們可以使用較低的語言版本編譯應用,這樣的話,我們就能夠繼續回滾到舊版本上。舉例來說,在 Maven 中:

<code><plugin>
<groupid>org.apache.maven.plugins/<groupid>
<artifactid>maven-compiler-plugin/<artifactid>
<version>3.8.0/<version>
<configuration>
<release>8/<release>
/<configuration>
/<plugin>
/<code>

在 Gradle 中:

<code>sourceCompatibility= JavaVersion.VERSION_1_8
/<code>

開始使用新特性

如果所有的事情都運行正常,所有測試都能運行通過而且所有的功能都有很好的性能表現,甚至在生產環境安全運行一段時間之後,我們才能考慮使用新的語言特性。

另外,還需要記住,儘管 Java 9 版本完全是關於 Java 模塊系統的,但是應用程序並不一定必須要使用它,即便我們已經遷移到了支持該功能的版本上的時候依然如此。不過,如果你對採用模塊系統感興趣的話,我們在 InfoQ 上有一個使用指南

總結

Java 8 之後,發生了很多的變化:每六個月就會有一個發佈版本;許可證、更新和支持方面都發生了變化;JDK 的來源可能也改變了。除此之外,當然還有新的語言特性,包括 Java 9 所帶來的重大變化。但是,現在 Java 11 取代 Java 8,成為了最新的 LTS 版本,主要的庫、框架和構建工具都採用了最新版本的 Java,所以這是將應用升級至 Java 11 或 12 好時機。

在度過了第一次升級的艱難時期之後,我們至少可以每六個月在最新版本的 Java 上測試一下我們的應用,比如在 CI 環境中。理想情況下,你甚至可以跟隨六個月的發佈節奏,這樣每六個月都能使用最新版本的 Java 並且能夠在第一時間使用新的特性。

小編已經整理好了,最新最火最全的學習資料,有需要的私聊小編,“資料”就可以免費獲得!


分享到:


相關文章: