譯者 | 劉汪洋
審校 | 重樓
學(xué)習(xí) Java 的過(guò)程中,我意識(shí)到在 90 年代末 OOP 正值鼎盛時(shí)期,Java 作為能夠真正實(shí)現(xiàn)這些概念的語(yǔ)言顯得尤為突出(盡管我此前學(xué)過(guò) C++,但相比 Java 影響較小)。我特別欣賞 Java 的平臺(tái)獨(dú)立性。
相比簡(jiǎn)單性,我更看重結(jié)構(gòu)和一致性,這也是我堅(jiān)持使用 Java 的主要原因。
在我的職業(yè)生涯中,我遇到了一些質(zhì)量不高的 Java 代碼庫(kù),這讓我對(duì) Java 產(chǎn)生了些失望。然而,在參與了許多其他優(yōu)秀的項(xiàng)目之后,我重新燃起了對(duì) Java 的熱愛(ài)。
我注意到那些批評(píng)和抱怨 Java 的通常是一些年輕人,他們接觸 JavaScript 的機(jī)會(huì)更多。與JavaScript 相比,Java 可能看起來(lái)更加笨重和受限——充斥著模板代碼,擁有嚴(yán)格的類(lèi)型系統(tǒng)等等。但如果讓我選擇,我無(wú)疑會(huì)更愿意處理一個(gè)次優(yōu)的 Java 代碼庫(kù),而不是 JavaScript 的。
只有在你積累了一定的工作經(jīng)驗(yàn),處理過(guò)分散在幾十個(gè)甚至上百個(gè)文件中的代碼后,你才會(huì)開(kāi)始意識(shí)到,Java 所謂的“限制”其實(shí)是為了防止你陷入困境的安全措施。
“只有兩種編程語(yǔ)言:被人們抱怨的和沒(méi)人使用的。”這是 C++ 之父 Bjarne Stroustrup 的著名言論。
關(guān)于 Java 的看法,確實(shí)存在不少爭(zhēng)議。相比之下,C++ 也有它的問(wèn)題。這種語(yǔ)言深受愛(ài)恨交織的評(píng)價(jià),仿佛是一種難以擺脫的復(fù)雜關(guān)系。
Python 也同樣面臨批評(píng)。
很多討論集中在全局解釋器鎖(GIL)上,它被認(rèn)為是阻礙有效多線(xiàn)程實(shí)現(xiàn)的主要障礙。由于Python 核心很多部分都依賴(lài)于 GIL,因此它可能無(wú)法被完全移除。為了解決這一問(wèn)題,開(kāi)發(fā)者不得不通過(guò)使用多個(gè)進(jìn)程和創(chuàng)建消息傳遞機(jī)制來(lái)實(shí)現(xiàn)進(jìn)程間的通信。此外,為了解決性能瓶頸,除非對(duì)關(guān)鍵代碼使用C/C++進(jìn)行重寫(xiě),否則 Python 的執(zhí)行速度通常較慢。當(dāng)然,Python 2 到 3 的過(guò)渡也引起了一些問(wèn)題。
此外,我曾經(jīng)參與過(guò)一個(gè) Django 項(xiàng)目。那時(shí),我認(rèn)為 Python 相比于類(lèi)型嚴(yán)格的語(yǔ)言更有優(yōu)勢(shì),盡管它適用于某些場(chǎng)景,但并不適合那些有著成千上萬(wàn)個(gè)類(lèi)的復(fù)雜系統(tǒng)。
當(dāng)這個(gè)項(xiàng)目由我一人擴(kuò)展至多人參與,代碼量超過(guò) 1 萬(wàn)行時(shí),它變得極其難以維護(hù)。
之后我轉(zhuǎn)向 Java,這是一個(gè)令人開(kāi)眼界的經(jīng)歷。它讓我深刻認(rèn)識(shí)到自己對(duì) Java 及其生態(tài)系統(tǒng)的熱愛(ài)。因此,我決定記錄下我所喜愛(ài)的 Java 生態(tài)系統(tǒng)方面的內(nèi)容。這樣,當(dāng)有人對(duì) Java 有所非議時(shí),你就有 25 個(gè)理由來(lái)反駁他們。
1. 成熟的生態(tài)系統(tǒng)
Java 已經(jīng)發(fā)展了 25 多年,作為一個(gè)在這個(gè)生態(tài)系統(tǒng)中工作的開(kāi)發(fā)者,回顧其成熟過(guò)程非常有趣。
Java 廣泛的生態(tài)系統(tǒng)最大的優(yōu)勢(shì)在于它提供了豐富的庫(kù)、構(gòu)建工具和框架選擇。
JVM 生態(tài)系統(tǒng)極為豐富,幾乎對(duì)于每個(gè)問(wèn)題都有一個(gè)最佳的庫(kù)解決方案,而且這些庫(kù)通常都表現(xiàn)出高性能并得到良好維護(hù)。在構(gòu)建工具方面,也有多種選擇,例如 Gradle、Maven 和 Bazel 等,它們能夠提供快速且可復(fù)制的構(gòu)建過(guò)程。對(duì)于那些不太熟悉 Java 生態(tài)系統(tǒng)的人來(lái)說(shuō),Java 還為日志記錄、數(shù)據(jù)庫(kù)連接、消息傳遞和應(yīng)用服務(wù)器等各種功能提供了默認(rèn)實(shí)現(xiàn),這些都是非常好的入門(mén)點(diǎn)。
以日志記錄為例,假設(shè)你的應(yīng)用程序需要進(jìn)行日志記錄。Java 提供了與 JDK 無(wú)縫集成的默認(rèn)日志記錄選項(xiàng)。如果你對(duì)默認(rèn)選項(xiàng)不滿(mǎn)意,或覺(jué)得它不夠好,那么你還可以選擇其他優(yōu)秀的日志記錄庫(kù),因?yàn)槟J(rèn)的日志記錄僅僅是日志記錄 API 的一個(gè)基本實(shí)現(xiàn)。
不僅僅是日志記錄,Java 生態(tài)系統(tǒng)還為數(shù)據(jù)庫(kù)連接、消息傳遞、應(yīng)用服務(wù)器、Servlets 等提供了豐富的選擇。
2. “一次編寫(xiě),到處運(yùn)行”(WORA)
“一次編寫(xiě),到處運(yùn)行”是用來(lái)描述 Java 語(yǔ)言跨平臺(tái)優(yōu)勢(shì)的常用口號(hào)。現(xiàn)在學(xué)習(xí) Java 的許多開(kāi)發(fā)者可能沒(méi)有意識(shí)到這一功能對(duì)軟件開(kāi)發(fā)的重大意義。
讓我們來(lái)回顧一下背景。在 Java 誕生的十年前,C++ 是主流的編程語(yǔ)言。然而,開(kāi)發(fā)者們面臨的一個(gè)主要挑戰(zhàn)是 C++ 的平臺(tái)依賴(lài)性。用 C++ 編寫(xiě)的代碼往往需要針對(duì)不同的操作系統(tǒng)或硬件架構(gòu)進(jìn)行重新編譯和修改。
Geeks for Geeks
3. 向后兼容性
想象一下,如果每次 Java 發(fā)布新版本,都要求你重寫(xiě)程序代碼,那將是多么昂貴和耗時(shí)的過(guò)程,尤其是對(duì)于大型組織而言。
Java 已經(jīng)發(fā)展多年,這意味著有許多基于舊版本 Java 構(gòu)建的軟件產(chǎn)品,它們是許多企業(yè)的核心,扮演著關(guān)鍵角色。
在企業(yè)級(jí)開(kāi)發(fā)中,項(xiàng)目規(guī)模通常龐大且復(fù)雜,這些系統(tǒng)要遷移到最新的 Java 版本需要細(xì)致的規(guī)劃和執(zhí)行。
Java 對(duì)向后兼容性的承諾極為重要,它向那些投入巨大資源開(kāi)發(fā)系統(tǒng)的開(kāi)發(fā)者或組織保證,他們的系統(tǒng)可以持續(xù)運(yùn)行并維護(hù),而無(wú)需進(jìn)行全面重寫(xiě)。Java(JVM)的向后兼容性也簡(jiǎn)化了遷移過(guò)程,促進(jìn)了新功能和改進(jìn)的采用,同時(shí)保障了現(xiàn)有系統(tǒng)的穩(wěn)定性。
4. Java 的強(qiáng)類(lèi)型系統(tǒng)
Java 是一種強(qiáng)類(lèi)型語(yǔ)言,與 Python 等松散類(lèi)型的語(yǔ)言形成對(duì)比。如果你使用過(guò) Python,你會(huì)立刻感受到將不同類(lèi)型的值賦給同一變量的靈活性,而語(yǔ)言會(huì)動(dòng)態(tài)適應(yīng)。
int age = 25;
String name = "John";
但這種靈活性是有代價(jià)的。我曾在一個(gè)涉及復(fù)雜計(jì)算的金融應(yīng)用程序上工作,該程序包含不同的數(shù)值數(shù)據(jù)類(lèi)型。Java 的強(qiáng)類(lèi)型特性意味著,編譯器會(huì)標(biāo)出任何嘗試混合不兼容數(shù)據(jù)類(lèi)型的操作,或是執(zhí)行可能導(dǎo)致數(shù)據(jù)丟失或意外結(jié)果的行為。而使用像 Python 這樣的語(yǔ)言時(shí),一些這樣的明顯錯(cuò)誤可能在運(yùn)行時(shí)被忽略。
這也是 Java 在開(kāi)發(fā)企業(yè)級(jí)應(yīng)用程序,尤其是在像銀行和金融等對(duì)可靠性和安全性有高要求的領(lǐng)域中,格外受歡迎的一個(gè)原因。除了減少運(yùn)行時(shí)錯(cuò)誤的需求,Java 的強(qiáng)類(lèi)型系統(tǒng)通過(guò)明確變量、參數(shù)和返回值的預(yù)期數(shù)據(jù)類(lèi)型,提高了代碼的可讀性。
5. 更快的發(fā)布周期 - 持續(xù)改進(jìn)
過(guò)去,作為 Java 開(kāi)發(fā)者,我們習(xí)慣于每隔幾年等待一次重大更新來(lái)獲得新的 Java 特性。但為了適應(yīng)現(xiàn)代編程的需求,自 Java 9 發(fā)布以后,Java 的發(fā)布節(jié)奏改為了每六個(gè)月一次。對(duì)于那些不急于迅速遷移到新版本的企業(yè)組織,Oracle 提出了每三年發(fā)布一個(gè)長(zhǎng)期支持(LTS)版本的策略。
更頻繁且規(guī)模更小的發(fā)布減少了升級(jí)到新 Java 版本的復(fù)雜性和風(fēng)險(xiǎn)。開(kāi)發(fā)者不太可能遇到重大的兼容性問(wèn)題,因?yàn)檫@些漸進(jìn)的變化被設(shè)計(jì)為更加向后兼容。
6. 優(yōu)秀的集成開(kāi)發(fā)環(huán)境(IDE)
Java 經(jīng)歷了漫長(zhǎng)的發(fā)展,引入了許多變化和特性,使其非常適合現(xiàn)代開(kāi)發(fā)。然而,這一切都得益于像 IntelliJ IDEA、Eclipse 和 NetBeans 這樣的強(qiáng)大集成開(kāi)發(fā)環(huán)境(IDE)的支持。
我無(wú)法想象在沒(méi)有智能代碼補(bǔ)全、自動(dòng)重構(gòu)、無(wú)縫版本控制集成等功能的環(huán)境下進(jìn)行編程會(huì)是什么體驗(yàn)。然而,也很重要的是要認(rèn)識(shí)到這并非始終如此,尤其是在 Java 早期。
快速發(fā)展到如今,像 IntelliJ IDEA 和 Eclipse 這樣的現(xiàn)代 IDE 使 Java 開(kāi)發(fā)變得更加輕松。這些IDE 與 Maven 和 Gradle 等構(gòu)建工具無(wú)縫集成,處理編譯、依賴(lài)解析和項(xiàng)目管理。像智能代碼補(bǔ)全這樣的功能減少了 Java 編寫(xiě)的繁瑣性,內(nèi)置的靜態(tài)代碼分析工具簡(jiǎn)化了調(diào)試過(guò)程,而插件系統(tǒng)則允許開(kāi)發(fā)者根據(jù)個(gè)人偏好自定義他們的開(kāi)發(fā)環(huán)境。
7. GraalVM 原生鏡像的支持
我們已經(jīng)探討了 JVM 如何作為一個(gè)強(qiáng)大的平臺(tái)推動(dòng) Java 的輝煌。然而,JVM 長(zhǎng)期以來(lái)也面臨著啟動(dòng)速度慢的問(wèn)題,這在當(dāng)前追求微服務(wù)、無(wú)服務(wù)器計(jì)算及快速啟動(dòng)和資源優(yōu)化的開(kāi)發(fā)環(huán)境中顯得尤為突出。
為了解決這一問(wèn)題,業(yè)界已在減少內(nèi)存占用和加快 Java 應(yīng)用啟動(dòng)速度方面做出了不懈努力。在這其中,我特別關(guān)注的是 GraalVM 原生鏡像技術(shù)。Oracle GraalVM 是一種高性能 JDK,它通過(guò)采用 Graal 編譯器這一替代的即時(shí)編譯器(JIT),提升了 Java 及其他基于 JVM 應(yīng)用的性能。
GraalVM 還引入了一種原生鏡像工具,它能夠?qū)崿F(xiàn) Java 字節(jié)碼的提前編譯(AOT),從而達(dá)到幾乎瞬間啟動(dòng)應(yīng)用的效果。Graal 編譯器在此過(guò)程中既是 AOT 編譯器,又能生成原生可執(zhí)行文件。簡(jiǎn)而言之,它將 Java 字節(jié)碼轉(zhuǎn)化為原生可執(zhí)行文件。
以下是一個(gè)使用遞歸反轉(zhuǎn)字符串的簡(jiǎn)單 Java 程序示例:
public class Example {
public static void main(String[] args) {
String str = "Native Image is awesome";
String reversed = reverseString(str);
System.out.println("反轉(zhuǎn)后的字符串是:" + reversed);
}
public static String reverseString(String str) {
if (str.isEmpty())
return str;
return reverseString(str.substring(1)) + str.charAt(0);
}
}
你可以對(duì)這個(gè)程序進(jìn)行編譯,然后基于 Java 類(lèi)創(chuàng)建一個(gè)原生鏡像。
javac Example.java
native-image Example
原生鏡像構(gòu)建器會(huì)將 Example 類(lèi)提前編譯成一個(gè)獨(dú)立的可執(zhí)行文件 example
,并保存在當(dāng)前工作目錄中。您接下來(lái)可以直接運(yùn)行這個(gè)可執(zhí)行文件:
./example
原生可執(zhí)行文件體積小,啟動(dòng)迅速,同時(shí)顯著減少了 CPU 和內(nèi)存的需求,非常適合于容器和云部署環(huán)境,其中成本優(yōu)化至關(guān)重要。
隨著 JDK 21、項(xiàng)目 Loom 和 ZGC 的最新進(jìn)展,我對(duì)這些技術(shù)的發(fā)展充滿(mǎn)期待。GraalVM 的原生鏡像現(xiàn)在也支持虛擬線(xiàn)程。我們可以創(chuàng)建使用 Spring Boot(通過(guò) Spring Boot 3.2)和 Java 21 虛擬線(xiàn)程的 GraalVM 原生鏡像。
8. 開(kāi)源庫(kù)和框架的重要性
開(kāi)源庫(kù)和框架是 Java 在我的工具集中占據(jù)重要位置的核心原因之一。
圖片來(lái)源:Google
這些庫(kù)和框架為我提供了一系列易于集成的構(gòu)建模塊,省去了自行為常見(jiàn)功能重新設(shè)計(jì)和實(shí)現(xiàn)的工作。它們就像一個(gè)隨時(shí)可以取用的代碼商店,其中存放著大量經(jīng)過(guò)嚴(yán)格測(cè)試和精心編寫(xiě)的代碼。
這些庫(kù)的多樣性意味著我不再受限于單一的解決方案。我總能找到最適合我的需求的庫(kù)。開(kāi)源性質(zhì)還促進(jìn)了透明度和責(zé)任感,意味著我可以深入探究源代碼,了解其背后的工作機(jī)制,甚至可以做出自己的貢獻(xiàn)。
Java 的開(kāi)源庫(kù)涵蓋廣泛,包括用于 JSON 解析的庫(kù)(如 Jackson 和 Gson)、日志庫(kù)(如 Log4j、SLF4j 和 LogBack)、單元測(cè)試庫(kù)(如 JUnit、Mockito 和 PowerMock),還有數(shù)據(jù)庫(kù)連接庫(kù)、消息傳遞庫(kù)等。
Java 的廣泛框架生態(tài)系統(tǒng)也是其成功的部分原因。Spring 和 Spring Boot 是我偏愛(ài)的組合之一。此外,我還使用過(guò) Jakarta Faces、Struts、Hibernate 和 Quarkus 等其他框架。
9. 多線(xiàn)程的支持
Java 對(duì)多線(xiàn)程的支持允許我設(shè)計(jì)能夠同時(shí)處理多任務(wù)的應(yīng)用程序,涵蓋數(shù)據(jù)處理、用戶(hù)交互管理和后臺(tái)計(jì)算等方面。Java 通過(guò)實(shí)現(xiàn) Runnable 接口或繼承 Thread 類(lèi)來(lái)實(shí)現(xiàn)多線(xiàn)程功能。
Java 的 java.util.concurrent 包提供了一系列先進(jìn)的工具,進(jìn)一步便利了并發(fā)應(yīng)用程序的開(kāi)發(fā),包括 ExecutorService、ScheduledExecutorService、Future、CyclicBarrier 等。
public class MyRunnable implements Runnable {
public void run() {
// 在新線(xiàn)程中要執(zhí)行的代碼
}
}
MyRunnable myRunnable = new MyRunnable();
Thread myThread = new Thread(myRunnable);
myThread.start();
現(xiàn)代計(jì)算標(biāo)準(zhǔn)中,多核處理器已成為常態(tài),即一個(gè)芯片上集成了多個(gè)處理器核心。Java 對(duì)多線(xiàn)程的支持使得我們能夠充分利用多核 CPU 的能力,這意味著我們可以開(kāi)發(fā)出更高性能的 Java 應(yīng)用程序,適用于游戲、視頻編輯、科學(xué)模擬等資源密集型活動(dòng)。
10. Java 面向?qū)ο蟮奶匦?/h3>
你可能會(huì)想,Java 并非唯一的面向?qū)ο缶幊陶Z(yǔ)言,它與 Python、C++ 等語(yǔ)言相比有何特殊之處?區(qū)別在于,Java 從一開(kāi)始就是基于面向?qū)ο笤瓌t設(shè)計(jì)的,而不是像其他語(yǔ)言那樣后來(lái)才加入面向?qū)ο缶幊蹋∣OP)的元素或支持。
Java 對(duì)面向?qū)ο缶幊痰乃拇笤瓌t——抽象、繼承、多態(tài)和封裝——的堅(jiān)持,使它成為構(gòu)建復(fù)雜、可擴(kuò)展和易于維護(hù)的軟件系統(tǒng)的理想選擇。我認(rèn)為,Java 對(duì) OOP 范式的深入支持帶來(lái)了許多優(yōu)勢(shì),如有助于構(gòu)建模塊化、靈活、易讀、易維護(hù)和可擴(kuò)展的應(yīng)用程序。
11. 內(nèi)存管理和垃圾回收
圖片來(lái)源:Digital Ocean
讓我們以一個(gè)類(lèi)比來(lái)看待這個(gè)問(wèn)題:就像許多人都不喜歡倒垃圾一樣,我也不喜歡手動(dòng)管理內(nèi)存。在需要手動(dòng)內(nèi)存管理的情況下,開(kāi)發(fā)者需要負(fù)責(zé)分配和釋放內(nèi)存,這就像決定何時(shí)倒垃圾一樣。如果忘記釋放內(nèi)存,就會(huì)導(dǎo)致內(nèi)存泄漏和性能問(wèn)題。
而 Java 的自動(dòng)內(nèi)存管理則像是一個(gè)可靠的垃圾回收服務(wù)。在 Java 中,垃圾收集器自動(dòng)識(shí)別并處理不再需要的內(nèi)存,從繁瑣的內(nèi)存管理中釋放了開(kāi)發(fā)者。
在使用 C++ 的過(guò)程中,我體驗(yàn)到了內(nèi)存管理的兩面性。C++ 提供了對(duì)內(nèi)存管理的細(xì)致控制,但這也給開(kāi)發(fā)者帶來(lái)了避免內(nèi)存泄漏的巨大責(zé)任。
相比之下,Java 讓開(kāi)發(fā)者不必?fù)?dān)心底層系統(tǒng)的細(xì)節(jié),也無(wú)需手動(dòng)進(jìn)行垃圾回收、處理底層操作系統(tǒng)的問(wèn)題或追蹤內(nèi)存分配與釋放。垃圾收集器會(huì)自動(dòng)識(shí)別并回收不再使用的內(nèi)存,減少了內(nèi)存泄漏的風(fēng)險(xiǎn)。
作為開(kāi)發(fā)者,這意味著我可以將更多精力集中在業(yè)務(wù)邏輯和應(yīng)用程序的高層設(shè)計(jì)上。如果你是團(tuán)隊(duì)中的一員,Java 的自動(dòng)內(nèi)存管理還可以提升開(kāi)發(fā)周期的效率和整體生產(chǎn)力。
12. 可觀測(cè)性和監(jiān)控
在過(guò)去,很多開(kāi)發(fā)者主要致力于單體應(yīng)用的開(kāi)發(fā)和維護(hù)。處理故障和修復(fù)漏洞相對(duì)直接,因?yàn)檎麄€(gè)應(yīng)用都集中在一個(gè)統(tǒng)一的代碼庫(kù)中。這就像在一張?jiān)敱M的地圖上導(dǎo)航,追蹤問(wèn)題相對(duì)容易。
然而,隨著微服務(wù)、無(wú)服務(wù)器計(jì)算和分布式系統(tǒng)的興起,情況發(fā)生了顯著的變化。由于微服務(wù)作為獨(dú)立服務(wù)通過(guò)網(wǎng)絡(luò)通信,識(shí)別和解決問(wèn)題變得更加復(fù)雜。當(dāng)問(wèn)題發(fā)生時(shí),它們可能不再局限于單個(gè)代碼庫(kù)。以下是我在使用 Java 進(jìn)行開(kāi)發(fā)時(shí),最青睞的一些監(jiān)控工具。
標(biāo)準(zhǔn)分析工具:
VisualVM.
VisualVM 對(duì)我來(lái)說(shuō)就像是探索 Java 應(yīng)用內(nèi)部世界的可靠伙伴。它融合了 JConsole 和 VisualGC 的功能,為我提供了一個(gè)用于監(jiān)控線(xiàn)程、堆使用情況和 CPU 性能的可視化平臺(tái)。此外,它與各種 JDK 工具的兼容性極佳,是一款十分可靠的監(jiān)控工具。
YourKit.
YourKit 在我的工具箱中就像一位秘密特工。它能夠深入到方法級(jí)別,揭示程序的執(zhí)行時(shí)間和內(nèi)存分配情況。它提供了一種簡(jiǎn)單、易用且安全的方法,在云環(huán)境、容器和集群中進(jìn)行性能分析。
應(yīng)用性能監(jiān)控(APM)工具:
New Relic.
New Relic 是我的首選應(yīng)用性能監(jiān)控工具。它就像一個(gè)全天候監(jiān)視我的應(yīng)用程序的私人助手,提供從實(shí)時(shí)洞察到詳細(xì)的事務(wù)跟蹤。它的警報(bào)系統(tǒng)就像我的安全網(wǎng),確保我對(duì)任何異常行為及時(shí)獲得通知。
AppDynamics.
AppDynamics 是我的性能監(jiān)控藝術(shù)家!它采用全面的監(jiān)控方式,觀察并可視化整個(gè)技術(shù)棧,從數(shù)據(jù)庫(kù)和服務(wù)器到云原生和混合環(huán)境。我特別欣賞它如何幫助我理解性能對(duì)最終用戶(hù)體驗(yàn)的影響,讓它不僅僅是一個(gè)監(jiān)控工具,更是一種用戶(hù)滿(mǎn)意度的衡量工具。
日志解決方案:
Log4j.
Log4j 在日志框架界就像一位可靠的元老。無(wú)論遇到何種情況,它都能忠實(shí)地記錄下事件和錯(cuò)誤。其在配置 Appender 和過(guò)濾器方面的靈活性,使其成為適應(yīng)各種日志需求的堅(jiān)實(shí)選擇。
SLF4J.
SLF4J 就像我手頭的日志瑞士軍刀。它本身并不直接記錄日志,而是作為一個(gè)外觀層,允許我無(wú)縫切換不同的日志實(shí)現(xiàn)。這種靈活性使它成為在多個(gè)庫(kù)中進(jìn)行日志管理的理想選擇,尤其是當(dāng)這些庫(kù)有各自的日志偏好時(shí)。
Java 的可觀測(cè)性:
Digma:連續(xù)反饋(CF)。
在軟件開(kāi)發(fā)中,如果不能直觀地看到代碼在實(shí)際環(huán)境中的運(yùn)行效果,就難以做出明智的設(shè)計(jì)決策和評(píng)估更改的影響。Digma 通過(guò)在可觀測(cè)性和代碼之間建立聯(lián)系,為一種新型的開(kāi)發(fā)方法鋪平了道路。
Digma是一個(gè)連續(xù)反饋工具 ,旨在簡(jiǎn)化從 OpenTelemetry 可觀測(cè)性源收集和處理代碼數(shù)據(jù)的過(guò)程。Digma 作為 IDE 插件在本地運(yùn)行,邊編碼邊收集代碼的數(shù)據(jù),從追蹤到日志和指標(biāo),讓你能夠?qū)崟r(shí)捕捉問(wèn)題并獲取深入洞察。
Prometheus 和 Grafana
Prometheus 和 Grafana 是我不可或缺的動(dòng)態(tài)雙人組合。Prometheus 負(fù)責(zé)從應(yīng)用程序中抓取指標(biāo),而 Grafana 則將這些指標(biāo)轉(zhuǎn)換成美觀且可自定義的儀表板。這些工具的開(kāi)源性質(zhì)和活躍的社區(qū)使它們成為我日常可觀測(cè)性的重要組成部分。
Elastic Stack (ELK)
Elastic Stack 結(jié)合了基于 AI 和搜索的開(kāi)放、可擴(kuò)展的全棧可觀測(cè)性功能。Elasticsearch、Logstash 和 Kibana 一起構(gòu)成了一個(gè)強(qiáng)大的工具組合,用于搜索、分析和可視化日志。它們將日志、指標(biāo)和追蹤數(shù)據(jù)關(guān)聯(lián)起來(lái),為我提供了一個(gè)全面的調(diào)查工具包。
13. 函數(shù)式編程的支持
自從 Java 8 推出以來(lái),函數(shù)式編程已成為 Java 支持的另一種編程范式。在函數(shù)式編程中,函數(shù)被視為“一等公民”,這意味著它們可以被賦值給變量、作為參數(shù)傳遞,甚至作為返回值。
Java 中的函數(shù)式編程特性為編程語(yǔ)言增添了吸引力。對(duì)我而言,采用 Java 中的函數(shù)式編程特性是一場(chǎng)規(guī)則改變的游戲。Lambda 表達(dá)式和函數(shù)接口的引入,不僅讓我寫(xiě)出的代碼更加簡(jiǎn)潔,而且表達(dá)力更強(qiáng)。利用 Stream API,我的應(yīng)用程序得以在多核處理器上執(zhí)行并行處理,從而獲得性能上的提升。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
int sum = list.stream().reduce(0, (a, b) -> a + b);
System.out.println(sum);
}
}
我發(fā)現(xiàn),函數(shù)式編程所倡導(dǎo)的聲明式風(fēng)格使代碼更易于閱讀和理解。對(duì)不可變性的重視以及避免副作用的原則,對(duì)我編寫(xiě)的代碼產(chǎn)生了積極影響,使其更加可預(yù)測(cè)和易于維護(hù)。而在測(cè)試方面,純函數(shù)的普遍應(yīng)用使得測(cè)試工作更加容易。
14. Java 豐富的文檔
團(tuán)隊(duì)項(xiàng)目的經(jīng)歷讓人深刻理解文檔的重要性。對(duì)我個(gè)人來(lái)說(shuō),我認(rèn)為文檔就像是代碼的用戶(hù)手冊(cè),它解釋了代碼的作用、實(shí)現(xiàn)方式及其原因。對(duì)于初學(xué) Java 的人來(lái)說(shuō),這就像身邊有一個(gè)導(dǎo)師。
Java 文檔包括豐富的代碼示例、教程、開(kāi)發(fā)者指南和 API 文檔,你可以借此快速開(kāi)發(fā)原型,并將其擴(kuò)展到真實(shí)世界的應(yīng)用程序。
文檔的時(shí)效性同樣重要。Java 的文檔始終保持最新,定期進(jìn)行修訂,以反映生態(tài)系統(tǒng)中的新發(fā)展。這些文檔由開(kāi)發(fā)者和專(zhuān)家編寫(xiě),其結(jié)構(gòu)也便于查找特定類(lèi)別、方法或概念的信息。
15. 構(gòu)建工具和依賴(lài)管理
在常規(guī)的 Java/Spring Boot 項(xiàng)目中,通常會(huì)遇到幾十甚至幾百個(gè)直接和間接依賴(lài)。嘗試手動(dòng)管理這些依賴(lài)可能會(huì)帶來(lái)極大的挑戰(zhàn),尤其是在處理大型企業(yè)項(xiàng)目時(shí),問(wèn)題如版本兼容性等更加復(fù)雜。使用構(gòu)建工具可以在團(tuán)隊(duì)成員之間統(tǒng)一構(gòu)建過(guò)程,確保每個(gè)開(kāi)發(fā)人員的本地運(yùn)行環(huán)境保持一致。
image.png
構(gòu)建工具如 Maven 和 Gradle 極大簡(jiǎn)化了構(gòu)建、測(cè)試和管理依賴(lài)的過(guò)程,為開(kāi)發(fā)人員節(jié)約了寶貴的時(shí)間和精力。這些工具能自動(dòng)從倉(cāng)庫(kù)獲取依賴(lài)項(xiàng)并檢查更新,省去了跟蹤依賴(lài)項(xiàng)更新和安全補(bǔ)丁的麻煩。
此外,構(gòu)建工具還為項(xiàng)目結(jié)構(gòu)和配置強(qiáng)制實(shí)施一定的約定和標(biāo)準(zhǔn),使項(xiàng)目易于理解和與其他 Java 開(kāi)發(fā)人員協(xié)作。
16. 強(qiáng)大的測(cè)試功能
雖然我們開(kāi)發(fā)人員可能害怕解決漏洞和與 QA 工程師合作,但我們也認(rèn)識(shí)到全面測(cè)試是確保我們的應(yīng)用程序盡可能無(wú)漏洞的關(guān)鍵方法之一。
選擇一個(gè)具有強(qiáng)大測(cè)試功能的編程語(yǔ)言可以大大減輕開(kāi)發(fā)負(fù)擔(dān),并有助于構(gòu)建更可靠、易于維護(hù)的代碼庫(kù)。這也是為什么我推崇 Java 作為構(gòu)建穩(wěn)定軟件的首選編程語(yǔ)言的原因之一。
在 Java 領(lǐng)域,無(wú)論是單元測(cè)試、集成測(cè)試還是端到端測(cè)試,都有一整套成熟的工具集來(lái)編寫(xiě)全面的測(cè)試用例。在這些工具中,JUnit 被廣泛認(rèn)為是單元測(cè)試的行業(yè)標(biāo)準(zhǔn)。它提供了一種簡(jiǎn)潔且高效的方式來(lái)撰寫(xiě)和執(zhí)行測(cè)試。JUnit 的核心特點(diǎn)是使用各種注解,如 @Test、@Before、@After、@BeforeClass 和 @AfterClass,來(lái)定義測(cè)試方法的生命周期。這種設(shè)計(jì)讓開(kāi)發(fā)者在執(zhí)行測(cè)試前能夠輕松地設(shè)定必要的前置條件。
// EmployeeServiceTest.java
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
@SpringBootTest
public class EmployeeServiceTest {
@Mock
private EmployeeRepository employeeRepository;
@InjectMocks
private EmployeeService employeeService;
@Test
public void testGetAllEmployees() {
// 模擬倉(cāng)庫(kù)響應(yīng)
when(employeeRepository.findAll()).thenReturn(new ArrayList<>());
// 測(cè)試 getAllEmployees 方法
List<Employee> result = employeeService.getAllEmployees();
// 斷言
assertEquals(0, result.size());
// 驗(yàn)證倉(cāng)庫(kù)方法調(diào)用情況
verify(employeeRepository, times(1)).findAll();
}
}
使用 JUnit 編寫(xiě)測(cè)試既簡(jiǎn)潔又高效,同時(shí)它能夠與眾多流行的 Java 集成開(kāi)發(fā)環(huán)境(IDE),如 Eclipse、IntelliJ IDEA 和 NetBeans 完美兼容。
在 Java 生態(tài)系統(tǒng)中,還有其他幾種對(duì)測(cè)試非常有幫助的工具。例如,TestNG 適用于集成測(cè)試,Cucumber 便于實(shí)現(xiàn)行為驅(qū)動(dòng)開(kāi)發(fā),而 Selenium 則專(zhuān)門(mén)用于自動(dòng)化功能測(cè)試和回歸測(cè)試用例的編寫(xiě)。此外,Mockito 作為一款功能強(qiáng)大的模擬框架,能夠與 JUnit、TestNG 等其他測(cè)試框架結(jié)合使用,以提高測(cè)試的靈活性和效率。
17. 龐大的社區(qū)
Java 社區(qū)在我作為一名 Java 開(kāi)發(fā)者的成長(zhǎng)過(guò)程中起著至關(guān)重要的作用。我已經(jīng)無(wú)數(shù)次從這個(gè)社區(qū)中獲得了幫助和指導(dǎo)。無(wú)論我面臨著一個(gè)棘手的問(wèn)題、探索新的庫(kù),還是尋求在實(shí)施新方案時(shí)的最佳實(shí)踐,Java 社區(qū)都是一個(gè)寶貴的知識(shí)源泉。
Java 社區(qū)是最活躍和響應(yīng)迅速的社區(qū)之一,我?guī)缀蹩偸悄軌蜓杆購(gòu)纳鐓^(qū)中獲得幫助。例如,在 Reddit 的 r/java 社區(qū),有數(shù)以萬(wàn)計(jì)的 Java 開(kāi)發(fā)者交流和分享。在 Stack Overflow、Github 等平臺(tái)上,從初學(xué)者到資深專(zhuān)業(yè)人士的各種開(kāi)發(fā)者都活躍在這些社區(qū)中。這種多樣性是社區(qū)強(qiáng)大的基石,意味著你能夠獲得來(lái)自不同領(lǐng)域和經(jīng)驗(yàn)層次的豐富專(zhuān)業(yè)知識(shí)。
除了在線(xiàn)社區(qū),許多 Java 活動(dòng)、會(huì)議和聚會(huì)為開(kāi)發(fā)者提供了面對(duì)面交流、分享經(jīng)驗(yàn)和學(xué)習(xí)的機(jī)會(huì)。這些聚會(huì)加強(qiáng)了人際網(wǎng)絡(luò)和協(xié)作,并且為 Java 開(kāi)發(fā)者間的社群感做出了貢獻(xiàn)。
18. Java 注解的支持
Java 注解自 Java 5 引入以來(lái),一直是一個(gè)受歡迎且有爭(zhēng)議的話(huà)題。注解的引入標(biāo)志著從 Hibernate 或 Spring XML 配置文件的廣泛使用向更現(xiàn)代化的編程方法轉(zhuǎn)變。注解使我們能夠?qū)⑿畔ⅰ⒅噶罨蚺渲弥苯忧度氲皆创a中,放置在所需的確切位置。
// 預(yù)定義注解
@Deprecated
@Override
@SuppressWarnings
@SafeVarargs
@FunctionalInterface
// 元注解
@Retention
@Documented
@Target
@Inherited
@Repeatable
// 自定義注解
// 也可以創(chuàng)建我們自己的自定義注解。
同時(shí),我們也能夠創(chuàng)建自己的自定義注解。
Java 注解無(wú)疑增強(qiáng)了代碼的清晰度和表達(dá)性,減少對(duì)外部文檔的依賴(lài)。注解還有助于減少某些樣板代碼,例如,通過(guò)注解,我們可以定義依賴(lài)注入、ORM 映射和事務(wù)邊界等方面的功能。
盡管注解方便并具有多種優(yōu)勢(shì),但一些開(kāi)發(fā)者對(duì)某些注解還持懷疑態(tài)度。
19. 安全功能
我們編寫(xiě)的程序不僅包含代碼行,還處理著用戶(hù)個(gè)人數(shù)據(jù)、財(cái)務(wù)信息和專(zhuān)有商業(yè)資料等敏感信息。用戶(hù)期望我們保護(hù)他們的信息,提供安全的使用環(huán)境。對(duì)企業(yè)而言,特別是那些涉及軟件開(kāi)發(fā)或創(chuàng)新領(lǐng)域的企業(yè),防止知識(shí)產(chǎn)權(quán)泄露尤為關(guān)鍵。有效保護(hù)源代碼、算法和專(zhuān)有信息對(duì)于維持企業(yè)的競(jìng)爭(zhēng)優(yōu)勢(shì)非常重要。
Java 提供了許多功能,使得開(kāi)發(fā)安全應(yīng)用程序變得更加容易。這些功能包括加密、公鑰基礎(chǔ)設(shè)施(PKI)、安全通信、認(rèn)證和訪(fǎng)問(wèn)控制等。此外,您還可以使用豐富的 API 和工具,輕松地將安全措施集成到您的 Java 應(yīng)用程序中,涵蓋從加密到智能卡 I/O 以及其他確保通信安全的認(rèn)證協(xié)議。
要了解更多可利用的安全功能,您可以參閱官方 Java 安全指南。
20. 豐富的 API 集
image.png
Java 以其豐富的應(yīng)用程序編程接口(API)著稱(chēng),為開(kāi)發(fā)者提供了與各種軟件組件、庫(kù)和服務(wù)進(jìn)行標(biāo)準(zhǔn)化交互的方式。這些 API 提供了豐富的類(lèi)、接口和方法集合,以便直接使用。
假設(shè)你正在從零開(kāi)始構(gòu)建一輛汽車(chē)。使用 Java API 就像是在組裝供應(yīng)商生產(chǎn)的零部件,而不是自己制造這些零件。在這種情況下,您可以挑選完全滿(mǎn)足您需求的 API。這些 API 是標(biāo)準(zhǔn)化的、文檔完備的,且易于使用。
Java API 的優(yōu)勢(shì)在于它抽象了組件構(gòu)建的復(fù)雜細(xì)節(jié),讓您能夠?qū)W⒂诮M裝一個(gè)完全功能的汽車(chē),即您的應(yīng)用程序。這些 API 能夠幫助您在網(wǎng)絡(luò)、IO、文件處理、數(shù)據(jù)庫(kù)連接、多線(xiàn)程等眾多領(lǐng)域完成各種任務(wù)。
Java API 分為多個(gè)包,其中一些最常用的包括 java.lang、java.util、java.io和 java.net。
21. Java 的性能提升
我選擇 Java 作為主要編程語(yǔ)言,部分原因是它不斷提高的性能,這讓我對(duì) Java 更加忠誠(chéng)和信任。
Java 性能方面的進(jìn)展在解決問(wèn)題和構(gòu)建高性能應(yīng)用程序方面表現(xiàn)卓越,這些應(yīng)用能滿(mǎn)足現(xiàn)代客戶(hù)的需求。其中一些顯著的改進(jìn)包括:
Java 虛擬機(jī)(JVM)在每個(gè)新版本中都會(huì)推出一些重大優(yōu)化。這些優(yōu)化包括即時(shí)(JIT)編譯器的提升、垃圾收集機(jī)制的加強(qiáng),以及更為高效的運(yùn)行時(shí)分析,共同作用于提高程序執(zhí)行速度和降低內(nèi)存消耗。
Project Valhalla 引入的值類(lèi)型特性,為我們定義了更加高效、緊湊的數(shù)據(jù)結(jié)構(gòu),這不僅減少了內(nèi)存消耗,還提高了緩存的局部性。在處理大規(guī)模數(shù)據(jù)時(shí),這種改進(jìn)帶來(lái)了顯著的性能提升。
最近,JDK 21 的發(fā)布引入了 15 項(xiàng)新特性,其中包括關(guān)鍵的封裝機(jī)制 API、虛擬線(xiàn)程,以及字符串模板和結(jié)構(gòu)化并發(fā)的預(yù)覽功能,這些更新顯著提升了 Java 的整體性能和功能。
JDK 21 中的虛擬線(xiàn)程和其他特性的飛躍
22. 結(jié)構(gòu)化并發(fā)的發(fā)展
結(jié)構(gòu)化并發(fā) API,作為 JDK 21 中的一個(gè)預(yù)覽特性,旨在簡(jiǎn)化并發(fā)編程。它通過(guò)將不同線(xiàn)程中的相關(guān)任務(wù)視為單一工作單位,增強(qiáng)了錯(cuò)誤處理、取消、可靠性和可觀測(cè)性。這一特性并非意在替代 java.util.concurrent 中現(xiàn)有的并發(fā)工具。
結(jié)構(gòu)化并發(fā)特性將使開(kāi)發(fā)者能夠擺脫在使用如 ExecutorService 和 Future 這類(lèi)現(xiàn)有構(gòu)造進(jìn)行并發(fā)編程時(shí)所面臨的管理任務(wù)和子任務(wù)的復(fù)雜性。傳統(tǒng)上,任務(wù)和子任務(wù)之間缺乏固有的關(guān)系,這導(dǎo)致在錯(cuò)誤處理、取消和可觀測(cè)性方面的挑戰(zhàn)。提出的結(jié)構(gòu)化并發(fā)方法尋求將代碼的語(yǔ)法結(jié)構(gòu)與任務(wù)的運(yùn)行時(shí)層次結(jié)構(gòu)對(duì)齊,使并發(fā)編程更加可讀、可維護(hù)和可靠。這種方法在處理并發(fā)時(shí)將提供更清晰和更直觀的編程模型。
23. 虛擬線(xiàn)程
JDK 21 正式推出了 Virtual Threads(虛擬線(xiàn)程)這一特性,該特性最初在 JDK 19 和 JDK 20 中作為預(yù)覽功能亮相。傳統(tǒng)上,每一個(gè) java.lang.Thread 實(shí)例都綁定于一個(gè)平臺(tái)線(xiàn)程,與一個(gè)底層操作系統(tǒng)線(xiàn)程相關(guān)聯(lián)并貫穿其整個(gè)生命周期。
然而,虛擬線(xiàn)程帶來(lái)了一種新的編程范式。盡管 java.lang.Thread 的實(shí)例依然存在,但它們以一種不同的方式運(yùn)行,允許在底層操作系統(tǒng)線(xiàn)程上執(zhí)行 Java 代碼,而非獨(dú)占一個(gè)線(xiàn)程。這一創(chuàng)新實(shí)現(xiàn)了多個(gè)虛擬線(xiàn)程高效地共享同一個(gè)操作系統(tǒng)線(xiàn)程的能力。不同于平臺(tái)線(xiàn)程,虛擬線(xiàn)程不會(huì)占用珍貴的操作系統(tǒng)線(xiàn)程資源,并且它們的數(shù)量可以遠(yuǎn)超操作系統(tǒng)線(xiàn)程的限制。
虛擬線(xiàn)程是 JDK 21 中的一大亮點(diǎn),對(duì)于需要處理大量并發(fā)任務(wù)的應(yīng)用程序來(lái)說(shuō),尤其是任務(wù)數(shù)量可能超過(guò)數(shù)千的場(chǎng)景,它們能夠顯著提升程序性能。
如今,根據(jù)應(yīng)用程序的具體需求,開(kāi)發(fā)者可以在虛擬線(xiàn)程和傳統(tǒng)的平臺(tái)線(xiàn)程之間做出選擇。
24. Switch 語(yǔ)句的模式匹配
這一特性源于 JDK 17 的提案,并在 JDK 18、JDK 19 和 JDK 20 中經(jīng)歷了一系列的完善和改進(jìn)。現(xiàn)在,它將成為 JDK 21 的一部分,基于來(lái)自 Java 社區(qū)的反饋和經(jīng)驗(yàn),進(jìn)行了進(jìn)一步的優(yōu)化。
這個(gè)特性旨在增強(qiáng) switch 表達(dá)式和語(yǔ)句的功能和通用性。它著重于增強(qiáng) case 標(biāo)簽中模式的作用,以提供處理 null 值的更大靈活性,并通過(guò)要求模式匹配的 switch 語(yǔ)句全面覆蓋所有可能的輸入值,提高了 switch 語(yǔ)句的安全性。
如果你已經(jīng)在使用 switch 表達(dá)式和語(yǔ)句,無(wú)需擔(dān)憂(yōu);其目標(biāo)是確保現(xiàn)有代碼能夠繼續(xù)像目前一樣正常運(yùn)行,無(wú)需作出任何修改。
25. 字符串模板
JDK 21 終于引入了長(zhǎng)期以來(lái)開(kāi)發(fā)者期盼的字符串模板特性,支持字符串插值。此前,唯一的選擇是拼接多個(gè)字符串或使用 string.format,這些方式不免顯得繁瑣。但在 Java 21 中,這一切將成為過(guò)去。新特性允許開(kāi)發(fā)者將 Java 的字符串字面量和文本塊與字符串模板結(jié)合使用。
例如:
int x = 10;
int y = 20;
String s = STR."\{x} + \{y} = \{x + y}";
該特性的主要目的是簡(jiǎn)化動(dòng)態(tài)字符串的創(chuàng)建,通過(guò)在運(yùn)行時(shí)編譯表達(dá)的值來(lái)實(shí)現(xiàn)。它還旨在提高代碼的可讀性,并解決與 StringBuilder 和 StringBuffer 類(lèi)相關(guān)的冗余問(wèn)題。此外,字符串模板還旨在解決與其他編程語(yǔ)言中現(xiàn)有的字符串插值技術(shù)相關(guān)的安全問(wèn)題。
總結(jié)
Java 的易用性和性能都有了顯著的提升,這使得代碼更容易閱讀和維護(hù)。在 Java 中,你不必?fù)?dān)心自己或同事的代碼是否夠高級(jí)或復(fù)雜。我們也不妨承認(rèn),Java 開(kāi)發(fā)人員的收入通常很高,這是因?yàn)樵S多大型的公司和組織都選擇 Java 作為他們的主要編程語(yǔ)言,從而創(chuàng)造了很多的工作機(jī)會(huì)。我認(rèn)識(shí)的一些人還在使用 Java 8,他們對(duì)此很滿(mǎn)意。
常見(jiàn)問(wèn)題解答
為什么 Java 成為一種流行的編程語(yǔ)言?
Java 憑借其平臺(tái)獨(dú)立性、穩(wěn)健性以及強(qiáng)大的社區(qū)支持而廣受歡迎。它在從網(wǎng)站開(kāi)發(fā)到企業(yè)級(jí)應(yīng)用各個(gè)領(lǐng)域廣泛應(yīng)用。
使用 Java 進(jìn)行企業(yè)級(jí)應(yīng)用開(kāi)發(fā)有哪些優(yōu)勢(shì)?
Java 提供了穩(wěn)定性強(qiáng)、易于擴(kuò)展的開(kāi)發(fā)環(huán)境,支持多線(xiàn)程處理,并配備了眾多框架和庫(kù),例如 Spring Boot,這些特性極大地簡(jiǎn)化了企業(yè)級(jí)應(yīng)用的開(kāi)發(fā)。
Java 如何支持跨平臺(tái)開(kāi)發(fā)?
Java 通過(guò)“一次編寫(xiě),到處運(yùn)行”的理念實(shí)現(xiàn)了跨平臺(tái)兼容性,允許開(kāi)發(fā)者在任何裝有 Java 虛擬機(jī) (JVM) 的設(shè)備上運(yùn)行 Java 代碼。
譯者介紹
劉汪洋,51CTO社區(qū)編輯,昵稱(chēng):明明如月,一個(gè)擁有 5 年開(kāi)發(fā)經(jīng)驗(yàn)的某大廠高級(jí) Java 工程師,擁有多個(gè)主流技術(shù)博客平臺(tái)博客專(zhuān)家稱(chēng)號(hào)。
原文標(biāo)題:25 REASONS WHY JAVA IS STILL AROUND IN 2023,作者:Digma