每當(dāng)我告訴別人我一直在用Java工作時惜颇,大家的反應(yīng)都是:
“納尼皆刺!Java?為啥是Java凌摄?”
說實話羡蛾,本人剛開始的時候也是同樣的反應(yīng)。但是由于Java的類型安全锨亏,執(zhí)行性能和堅如磐石的工具痴怨,我漸漸地開始欣賞Java。同時我注意到器予,現(xiàn)在的Java已今非昔比——它在過去的10年間穩(wěn)健地改善著浪藻。
緣何是Java?
假 設(shè)每天都用Java的想法還沒有讓君惡心到食不下咽乾翔,我在此重申Java已非你所了解的“吳下阿蒙”了爱葵。當(dāng)Python, Ruby, 和Javascript在“動態(tài)類型語言革命”?(我自己造的名詞)中大放異彩時,Java已經(jīng)悄悄地借鑒了動態(tài)語言和函數(shù)式語言的很多吸引人的特性,同 時保留了讓Java和JVM晉級一流開發(fā)環(huán)境的先賢的努力成果萌丈。憑借大約9百萬Java攻城獅的基層群體赞哗,Java仍然是世界上最受歡迎的編程語言。我們 不能僅僅因為Java的語法有一點點繁瑣辆雾,就抹殺掉它所有的歷史和開發(fā)工作肪笋。但是流行不等同于正確。下面我們就來看看是什么讓Java如此大放異彩乾颁。
Java虛擬機(JVM)
Java虛擬機(JVM) 已經(jīng)誕生20年了涂乌。在此期間艺栈,它被部署在成千上萬的系統(tǒng)上英岭,歷經(jīng)了無數(shù)的漏洞修復(fù)和性能提升。JVM的優(yōu)點有以下幾個方面湿右。首先诅妹,JVM完美支持日志和監(jiān)控, 這使你可以很方便地監(jiān)控小到單個線程的性能指標(biāo)毅人。JVM有世界上最優(yōu)化的垃圾回收器之一吭狡,你可以根據(jù)優(yōu)化吞吐量等因素靈活選擇垃圾回收算法。最 后丈莺,Java承諾的“write once, run anywhere”終于得已實現(xiàn)——你可以輕松地在任何架構(gòu)上部署一個Java應(yīng)用(大家還是承認(rèn)applet從來沒有過吧)划煮。為什么用Scala和 Clojure這樣新式語言的聰明人會選擇JVM作為他們的執(zhí)行環(huán)境呢?——因為JVM為你的代碼提供了一個無出其右的分發(fā)環(huán)境缔俄。拋棄像JVM這樣堅如磐 石的工具是非常不合理的弛秋。
庫的支持
如 果你需要做點什么,很可能已經(jīng)有非常好用且經(jīng)過測試的Java庫在等著你俐载。Java庫大部分都是成熟并用于實際生產(chǎn)開發(fā)的蟹略。Google, Amazon, LinkedIn, Twitter和很多Apache項目都很倚重于Java。如果你用了Java遏佣,你可以參考這些庫和公司挖炬,從而借鑒偉大的程序員先驅(qū)們的工作。
類型安全
Java的類型系統(tǒng)状婶,雖然有時很繁瑣意敛,但是這使得你可以寫出“好用”的代碼。不再有運行調(diào)試膛虫,它使你可以依靠編譯器而不是單元測試——單元測試只在你知道bug在哪里的時候才有用空闲。類型安全也使你輕松的代碼重構(gòu)。Java同時支持范型——Go語言的最大詬病之一走敌。再者碴倾,Guava這樣的庫I以最小的樣板和開銷,標(biāo)準(zhǔn)化了創(chuàng)建類型安全的API的方法。 Java編譯器的改進(jìn)也意味著你可以在享受類型安全的同時最小化范型所需的樣板代碼跌榔。
并發(fā)性
下面這條tweet總結(jié)了大多數(shù)動態(tài)語言的并行狀態(tài):
Most JS/Python/Ruby apps… pic.twitter.com/hkDkjdxpFH
— Reuben Bond (@reubenbond)
Java 卻有著對多線程和并行的一流支持异雁。對于Java 1.7, 許并行的immutable數(shù)據(jù)結(jié)構(gòu)令你輕松地在線程間共享數(shù)據(jù)僧须。Akka庫更進(jìn)一步的提供了Erlang型的Actors來寫并發(fā)和分布式的程序纲刀。我并 不是在說Java比Go具有更好的并行支持,但是可以管理單個線程這一特性為Java應(yīng)用提供了異步性能担平;而Python是做不到這點的示绊。
用最新的Java來編程
現(xiàn)在你的心情可能已經(jīng)從惡心變成好奇了,那么我們在2015年該如何寫Java呢暂论?從哪兒開始呢面褐?首先,讓我們回顧一些在Java?7和Java?8涌現(xiàn)的核心語言概念取胎。
迭代
首先我們一起來看看迭代展哭。下面是Java?8中的?for循環(huán):
List names =newLinkedList<>();// compiler determines type of LinkedList// ... add some names to the collectionnames.forEach(name -> System.out.println(name));
或者是被大大簡化的 for關(guān)鍵詞?
for(Stringname : names)System.out.println(name);
這2種循環(huán)結(jié)構(gòu)都比你平時看到的for循環(huán)簡潔的多闻蛀。
Lambda函數(shù)
上面提到的第一個for循環(huán)引入了Lambda函數(shù)這個新概念匪傍。Lamda函數(shù),語法記作->, 是Java語言的一項重大改革觉痛,并從函數(shù)式編程中引入了一些概念役衡。
下面來看幾個Java中Lambda函數(shù)的例子。
// Lambda RunnableRunnabler2 = () ->System.out.println("Hello world two!");// Lambda SortingCollections.sort(personList, (Personp1,Personp2) -> p1.getSurName().compareTo(p2.getSurName()))// Lambda ListenertestButton.addActionListener(e ->System.out.println("Click Detected by Lambda Listener"));
這里無法詳細(xì)展開Lambda函數(shù)這個話題——http://www.drdobbs.com/jvm/lambda-expressions-in-java-8/240166764文章提供了一個很好的切入點來更多地了解Lambda函數(shù)薪棒。
流
Java 8引入了流(stream)的概念手蝎,這為Java提供了很多現(xiàn)代函數(shù)式語言的特性。流是一種對集合上的一系列轉(zhuǎn)換延遲執(zhí)行的機制盗尸。比如我們來數(shù)一下以’A’開頭的名字柑船。首先想到的方法肯定是像下面這樣:
List names = newLinkedList<>();// ... add some names to the collectionlongcount=0;for(Stringname : names)? {if(name.startsWith("A"))++count;}
如果用流,上述就可以簡化為首先將集合轉(zhuǎn)換成流泼各,然后使用函數(shù):
List names = newLinkedList<>();// ... add some names to the collectionlongcount= names.stream().filter(name -> name.startsWith("A")).count();
Java同時支持用parallelStream()來進(jìn)行流的并行處理鞍时。并行流允許流水線業(yè)務(wù)在獨立的線程同時執(zhí)行,這不僅改進(jìn)了語法扣蜻,同時提高了性能逆巍。在大多數(shù)情況下,你可以簡單得用parallelStream()替換stream()實現(xiàn)并行莽使。
Try-With-Resources結(jié)構(gòu)
在Java 6之前锐极,打開一個文件然后讀取內(nèi)容需要通過try/finally來完成:
staticStringreadFirstLineFromFileWithFinallyBlock(String path)throwsIOException{BufferedReader br =newBufferedReader(newFileReader(path));try{returnbr.readLine();}finally{if(br !=null) br.close();}}
但是readLine和close都有可能拋出異常。在這種情況下芳肌,readLine拋出的異常被忽略灵再,我們事實上并不知道readLine執(zhí)行失敗肋层。
Java 7引入了 Try-With-Resources結(jié)構(gòu)來克服這種缺陷:
staticStringreadFirstLineFromFile(String path)throwsIOException{try(BufferedReader br =newBufferedReader(newFileReader(path))) {returnbr.readLine();}}
上例中,無論在何種失敗情況下翎迁,BufferedReader都會自動關(guān)閉文件流栋猖。你可以通過用逗號分隔的方式,用一個try語句來打開多個資源汪榔。
多重catch
以往Java只允許一個catch代碼塊對應(yīng)一個異常蒲拉,這造成如下的代碼冗余:
catch(IOException ex) {logger.log(ex);throwex;catch(SQLException ex) {logger.log(ex);throwex;}
從Java 7開始,你可以在一個代碼塊內(nèi)捕捉多個異常痴腌,從而減少了代碼冗余:
catch(IOException|SQLException ex) {logger.log(ex);throwex;}
數(shù)值字面常量(Numeric Literals)
數(shù)值字面常量可以添加下劃線是Java語言的新特性雌团。這允許你使用_作為大數(shù)字的視覺分隔符。下面的例子不言自明:
intthousand =1_000;intmillion? =1_000_000;
使用Java
看到現(xiàn)代Java的語法如何簡化并擴展了老Java之后士聪,你可能已經(jīng)摩拳擦掌躍躍欲試Java了锦援。我整理了一下第三方的工具和庫,這些可以用來幫助你們上手戚嗅。
Maven
Maven是一個Java構(gòu)建系統(tǒng)雨涛,相比于配置枢舶,它更重視規(guī)范懦胞。Maven定義了應(yīng)用程序的結(jié)構(gòu),并提供了許多內(nèi)置函數(shù)凉泄,比如運行測試躏尉,打包應(yīng)用,部署你的庫后众。使用Maven會顯著降低管理Java項目的認(rèn)知開銷胀糜。?Maven Central是Java世界中的PyPI,為已發(fā)布的Java庫提供一站式服務(wù)蒂誉。
核心函數(shù)
谷歌的Guava library提供了谷歌Java開發(fā)中所使用的核心函數(shù)教藻。這包括應(yīng)用于集合,緩存右锨,基礎(chǔ)數(shù)據(jù)類型括堤,并發(fā),字符串處理工作绍移,I/O等的常見函數(shù)悄窃。Guava為如何設(shè)計好的的Java API提供了絕佳的案例分析,提供最有效的從Java中推薦的最佳實踐的具體例子一個很好的案例,?Effective Java中推薦的最佳實踐大部分都在Guava中得以體現(xiàn)蹂窖。Guava被用于谷歌產(chǎn)品開發(fā)轧抗,進(jìn)行了超過286,000個單元測試,可謂經(jīng)受過實戰(zhàn)測試的考驗瞬测。
日期/時間函數(shù)
Joda-Time?已 經(jīng)成為Java實際上的標(biāo)準(zhǔn)日期/時間函數(shù)庫横媚。事實上纠炮,Java 8幾乎一字不差地采用了Joda-Time規(guī)范。自此灯蝴,我們建議使用java.time中的日期/時間函數(shù)代替Joda-Time抗碰。但是,如果你需要使用 Java 8之前的版本绽乔,Joda-Time提供了無與倫比的API弧蝇。
分布式系統(tǒng)
Akka?提供類似Erlang型的Actor模型的抽象層來編寫分布式系統(tǒng)。Akka可以從容應(yīng)對許多種不同的故障折砸,為編寫可靠的分布式系統(tǒng)提供了更高層次的抽象看疗。
Web應(yīng)用程序
需要用Java寫一個功能完善的Web應(yīng)用程序?莫怕睦授,有Play Framework罩著你两芳。Play基于Akka的非阻塞I/O,提供了編寫Web應(yīng)用程序的可擴展的異步框架去枷。如果想使用不那么前沿但是被廣泛應(yīng)用于產(chǎn)品的框架怖辆,請嘗試Jetty。
單元測試
JUnit?仍為編寫單元測試的標(biāo)準(zhǔn)删顶。最近幾年竖螃,JUnit的匹配器有所擴展,允許你對集合作assertions逗余。例如特咆,您可以輕松地斷言一個鏈表是否包含某個特殊值。
模擬框架(Mocking Framework)
Mockito是Java的標(biāo)準(zhǔn)模擬庫录粱。它提供了所有你能想到的且對編寫測試非常重要的模擬庫的功能腻格。
然而不足的是。啥繁。菜职。
目前為止,我一直在為Java說好話旗闽,但是有些方面它還是很爛酬核。
它還是Java!
Java的歷史遺留不可避免宪睹,Java仍然向下兼容其最早的版本愁茁,這意味著語言和標(biāo)準(zhǔn)庫的最爛的部分還存在著。Guava是為了令Java語言更討人喜歡而產(chǎn)生這個事實就證明了亭病,Java和API存在不一致鹅很,令人困惑的問題,有時甚至是完全錯誤的罪帖。
JSON
Java缺少映射到JSON的object literal syntax(如Python的字典literal syntax)促煮。正因如此邮屁,從Java對象映射到JSON有時需要繁復(fù)的對象實例化和映射,反之亦然菠齿。目前有各種JSON庫在這個領(lǐng)域競爭佑吝,Jackson是當(dāng)前的最受歡迎的,但是Jackson的文檔需要進(jìn)一步完善绳匀。
模擬(Mocking)
Mockito解決了測試Java代碼中的很多痛點芋忿,但是從像Python語言的靈活轉(zhuǎn)換到Java語言的嚴(yán)格,你需要更謹(jǐn)慎地來設(shè)計你的類用于模擬疾棵。
REPL
我之所以喜歡Python戈钢,其中一點就是它可以迅速地實現(xiàn)讀取﹣求值﹣輸出循環(huán)(?read-eval-print loop),從而快速評估新的想法或檢驗假設(shè)是尔。雖然一直有聲音說要把讀取﹣求值﹣輸出循環(huán)添加到標(biāo)準(zhǔn)Java庫殉了,這一點目前還是不支持的。
語法累贅
雖然Java編譯器的進(jìn)步意味著明確的類型簽名不再那么需要——尤其對于泛型——但是Java仍然比Python冗余的多拟枚。啟動和運行一個項目需要更多的樣板和開銷——通常這意味更多的工作薪铜。
結(jié)論
Java擁有一個漫長而傳奇的歷史,其中有好有壞恩溅。如果你已經(jīng)很多年沒有使用Java工作了隔箍,也許現(xiàn)在是一個好機會再次嘗試它。只要不是像下面這樣做: