最近開始研究Java8济锄,現(xiàn)將其體驗(yàn)分享如下: ??
?1.為什么需要使用Java8? ? ?
1996 年1 月盗扒,Java 1.0 發(fā)布管钳,此后計(jì)算機(jī)編程領(lǐng)域發(fā)生了翻天覆地的變化榜晦。商業(yè)發(fā)展需要更復(fù)雜的應(yīng)用虏肾,大多數(shù)程序都跑在功能強(qiáng)大的多核CPU 的機(jī)器上廓啊。帶有高效運(yùn)行時(shí)編譯器的Java 虛擬機(jī)(JVM)的出現(xiàn),使程序員將更多精力放在編寫干凈封豪、易于維護(hù)的代碼上谴轮,而不是思考如何將每一個(gè)CPU 時(shí)鐘周期、每字節(jié)內(nèi)存物盡其用吹埠。多核CPU 的興起成為了不容回避的事實(shí)第步。涉及鎖的編程算法不但容易出錯(cuò),而且耗費(fèi)時(shí)間缘琅。人們開發(fā)了java.util.concurrent 包和很多第三方類庫(kù)粘都,試圖將并發(fā)抽象化,幫助程序員寫出在多核CPU 上運(yùn)行良好的程序刷袍。很可惜翩隧,到目前為止,我們的成果還遠(yuǎn)遠(yuǎn)不夠呻纹。? ? ? ??
開發(fā)類庫(kù)的程序員使用Java 時(shí)堆生,發(fā)現(xiàn)抽象級(jí)別還不夠专缠。處理大型數(shù)據(jù)集合就是個(gè)很好的例子,面對(duì)大型數(shù)據(jù)集合顽频,Java 還欠缺高效的并行操作藤肢。開發(fā)者能夠使用Java 8 編寫復(fù)雜的集合處理算法,只需要簡(jiǎn)單修改一個(gè)方法糯景,就能讓代碼在多核CPU 上高效運(yùn)行嘁圈。為了編寫這類處理批量數(shù)據(jù)的并行類庫(kù),需要在語(yǔ)言層面上修改現(xiàn)有的Java:增加Lambda 表達(dá)式蟀淮。? ? ? ??
當(dāng)然最住,這樣做是有代價(jià)的,程序員必須學(xué)習(xí)如何編寫和閱讀使用Lambda 表達(dá)式的代碼怠惶,但是涨缚,這不是一樁賠本的買賣。與手寫一大段復(fù)雜策治、線程安全的代碼相比脓魏,學(xué)習(xí)一點(diǎn)新語(yǔ)法和一些新習(xí)慣容易很多。開發(fā)企業(yè)級(jí)應(yīng)用時(shí)通惫,好的類庫(kù)和框架極大地降低了開發(fā)時(shí)間和成本茂翔,也為開發(fā)易用且高效的類庫(kù)掃清了障礙。? ? ? ?
對(duì)于習(xí)慣了面向?qū)ο缶幊痰拈_發(fā)者來(lái)說(shuō)履腋,抽象的概念并不陌生珊燎。面向?qū)ο缶幊淌菍?duì)數(shù)據(jù)進(jìn)行抽象,而函數(shù)式編程是對(duì)行為進(jìn)行抽象∽窈現(xiàn)實(shí)世界中悔政,數(shù)據(jù)和行為并存,程序也是如此延旧,因此這兩種編程方式我們都得學(xué)谋国。這種新的抽象方式還有其他好處。不是所有人都在編寫性能優(yōu)先的代碼垄潮,對(duì)于這些人來(lái)說(shuō)烹卒,函數(shù)式編程帶來(lái)的好處尤為明顯。程序員能編寫出更容易閱讀的代碼——這種代碼更多地表達(dá)了業(yè)務(wù)邏輯的意圖弯洗,而不是它的實(shí)現(xiàn)機(jī)制。易讀的代碼也易于維護(hù)逢勾、更可靠牡整、更不容易出錯(cuò)。? ? ?
?在寫回調(diào)函數(shù)和事件處理程序時(shí)溺拱,程序員不必再糾纏于匿名內(nèi)部類的冗繁和可讀性逃贝,函數(shù)式編程讓事件處理系統(tǒng)變得更加簡(jiǎn)單谣辞。能將函數(shù)方便地傳遞也讓編寫惰性代碼變得容易,惰性代碼在真正需要時(shí)才初始化變量的值沐扳。Java 8 還讓集合類可以擁有一些額外的方法:default 方法泥从。程序員在維護(hù)自己的類庫(kù)時(shí),可以使用這些方法沪摄。 ? ? ? ?
2.函數(shù)式編程? ? ?
每個(gè)人對(duì)函數(shù)式編程的理解不盡相同躯嫉。但其核心是:在思考問題時(shí),使用不可變值和函數(shù)杨拐,函數(shù)對(duì)一個(gè)值進(jìn)行處理祈餐,映射成另一個(gè)值。不同的語(yǔ)言社區(qū)往往對(duì)各自語(yǔ)言中的特性孤芳自賞『逄眨現(xiàn)在談Java 程序員如何定義函數(shù)式編程還為時(shí)尚早帆阳,但是,這根本不重要屋吨!我們關(guān)心的是如何寫出好代碼蜒谤,而不是符合函數(shù)式編程風(fēng)格的代碼。本書將重點(diǎn)放在函數(shù)式編程的實(shí)用性上至扰,包括可以被大多數(shù)程序員理解和使用的技術(shù)鳍徽,幫助他們寫出易讀、易維護(hù)的代碼渊胸。? ? ??
3.Lambda表達(dá)式? ? ??
Lambda 表達(dá)式是?? 一個(gè)匿名方法旬盯,將行為像數(shù)據(jù)一樣進(jìn)行傳遞。? ? ?
?A.Lambda表達(dá)式的常見結(jié)構(gòu):BinaryOperatoradd = (x, y) → x + y翎猛。
B.函數(shù)接口指僅具有單個(gè)抽象方法的接口胖翰,用來(lái)表示 Lambda表達(dá)式的類型。
4.流
內(nèi)部迭代將更多控制權(quán)交給了集合類切厘。
A.和 Iterator 類似萨咳,Stream 是一種內(nèi)部迭代方式。
B.將 Lambda表達(dá)式和 Stream 上的方法結(jié)合起來(lái)疫稿,可以完成很多常見的集合操作培他。
5.類庫(kù)
使用為基本類型定?? 制的 Lambda表達(dá)式和 Stream,如IntStream 可以顯著提升系統(tǒng)性能遗座。
A.默認(rèn)方法是指接口中定義的包含方法體的方法舀凛,方法名有 default 關(guān)鍵字做前綴。
B.在一個(gè)值可能為空的建模情況下途蒋,使用 Optional 對(duì)象能替代使用 null 值猛遍。
6.高級(jí)集合類和收集器
方法引用是一種引用方法的輕量級(jí)語(yǔ)法,形如:ClassName::methodName。
A.收集器可用來(lái)計(jì)算流的最終值懊烤,是 reduce 方法的模擬梯醒。
B.Java 8 提供了收集多種容器類型的方式,同時(shí)允許用戶自定義收集器腌紧。
7.數(shù)據(jù)并行化
數(shù)據(jù)并行化是把?? 工作拆分茸习,同時(shí)在多核 CPU上執(zhí)行的方式。
A.如果使用流編寫代碼壁肋,可通過(guò)調(diào)用 parallel 或者 parallelStream 方法實(shí)現(xiàn)數(shù)據(jù)并行化操作号胚。
B. 影響性能的五要素是:數(shù)據(jù)大小、源數(shù)據(jù)結(jié)構(gòu)墩划、值是否裝箱涕刚、可用的 CPU 核數(shù)量,以及處理每個(gè)元素所花的時(shí)間乙帮。
8.重構(gòu)代碼和測(cè)試
重構(gòu)遺留代碼時(shí)考慮如何使用?? Lambda 表達(dá)式杜漠,有一些通用的模式。
A.如果想要對(duì)復(fù)雜一點(diǎn)的 Lambda表達(dá)式編寫單元測(cè)試察净,將其抽取成一個(gè)常規(guī)的方法驾茴。
B.peek 方法能記錄中間值,在調(diào)試時(shí)非常有用氢卡。
9.設(shè)計(jì)架構(gòu)和原則
Lambda 表達(dá)式能讓很多現(xiàn)有設(shè)計(jì)模?? 式更簡(jiǎn)單锈至、可讀性更強(qiáng),尤其是命令者模式译秦。
A.在 Java 8 中峡捡,創(chuàng)建領(lǐng)域?qū)S谜Z(yǔ)言有更多的靈活性。
B.在 Java 8 中筑悴,有應(yīng)用 SOLID 原則的新機(jī)會(huì)们拙。
10.關(guān)于響應(yīng)式編程RxJava
構(gòu)建復(fù)雜并行操作的另外一種方案是使用Future。Future 像一張欠條阁吝,方法不是返回一個(gè)值砚婆,而是返回一個(gè)Future 對(duì)象,該對(duì)象第一次創(chuàng)建時(shí)沒有值突勇,但以后能拿它“換回”一個(gè)值装盯。調(diào)用Future 對(duì)象的get 方法獲取值,它會(huì)阻塞當(dāng)前線程甲馋,直到返回值埂奈。可惜定躏,和回調(diào)一樣挥转,組合Future 對(duì)象時(shí)也有問題海蔽,我們會(huì)快速瀏覽這些可能碰到的問題共屈。
這些問題的解決之道是CompletableFuture绑谣,它結(jié)合了Future 對(duì)象打欠條的主意和使用回調(diào)處理事件驅(qū)動(dòng)的任務(wù)。其要點(diǎn)是可以組合不同的實(shí)例拗引,而不用擔(dān)心末日金字塔問題。
CompletableFuture 背后的概念可以從單一的返回值推廣到數(shù)據(jù)流,這就是響應(yīng)式編程斑芜。響應(yīng)式編程其實(shí)是一種聲明式編程方法哥童,它讓程序員以自動(dòng)流動(dòng)的變化和數(shù)據(jù)流來(lái)編程。你可以將電子表格想象成一個(gè)使用響應(yīng)式編程的例子哼凯。如果在單元格C1 中鍵入=B1+5欲间,其實(shí)是在告訴電子表格將B1 中的值加5,然后將結(jié)果存入C1断部。而且猎贴,將來(lái)B1 中的值變化后,電子表格會(huì)自動(dòng)刷新C1 中的值蝴光。
RxJava 類庫(kù)將這種響應(yīng)式的理念移植到了JVM她渴。我們這里不會(huì)深入類庫(kù),只描述其中的一些關(guān)鍵概念蔑祟。
RxJava 類庫(kù)引入了一個(gè)叫作Observable 的類趁耗,該類代表了一組待響應(yīng)的事件,可以理解為一沓欠條疆虚。在Observable 對(duì)象和第3 章講述的Stream 接口之間有很強(qiáng)的關(guān)聯(lián)苛败。
兩種情況下,都需要使用Lambda 表達(dá)式將行為和一般的操作關(guān)聯(lián)径簿、都需要將高階函數(shù)鏈接起來(lái)定義完成任務(wù)的規(guī)則罢屈。實(shí)際上,Observable 定義的很多操作都和Stream 的相同:map牍帚、filter儡遮、reduce。
最大的不同在于用例暗赶。Stream 是為構(gòu)建內(nèi)存中集合的計(jì)算流程而設(shè)計(jì)的鄙币,而RxJava 則是為了組合異步和基于事件的系統(tǒng)流程而設(shè)計(jì)的。它沒有取數(shù)據(jù)蹂随,而是把數(shù)據(jù)放進(jìn)去十嘿。換個(gè)角度理解RxJava,它是處理一組值岳锁,而CompletableFuture 用來(lái)處理一個(gè)值绩衷。
11.何時(shí)使用新技術(shù)
新技術(shù)那么美好,這是否意味著大家明天就要扔掉現(xiàn)有的Java EE 或者Spring 企業(yè)級(jí)Web 應(yīng)用呢?答案當(dāng)然是否定的咳燕。
即使不去考慮CompletableFuture 和RxJava 相對(duì)較新勿决,使用它們依然有一定的復(fù)雜度。它們用起來(lái)比到處顯式使用Future 和回調(diào)簡(jiǎn)單招盲,但對(duì)很多問題來(lái)說(shuō)低缩,傳統(tǒng)的阻塞式Web 應(yīng)用開發(fā)技術(shù)就足夠了。如果還能用曹货,就別修理咆繁。
事件驅(qū)動(dòng)和響應(yīng)式應(yīng)用正在變得越來(lái)越流行,而且經(jīng)常會(huì)是為你的問題建模的最好方式之一顶籽。響應(yīng)式編程宣言(http://www.reactivemanifesto.org/)鼓勵(lì)大家使用這種方式編寫更多應(yīng)用玩般,如果它適合你的待解問題,那么就應(yīng)該使用礼饱。相比阻塞式設(shè)計(jì)坏为,有兩種情況可能特別適合使用響應(yīng)式或事件驅(qū)動(dòng)的方式來(lái)思考。
第一種情況是業(yè)務(wù)邏輯本身就使用事件來(lái)描述慨仿。Twitter 就是一個(gè)經(jīng)典例子久脯。Twitter 是一種訂閱文字流信息的服務(wù),用戶彼此之間推送信息镰吆。使用事件驅(qū)動(dòng)架構(gòu)編寫應(yīng)用帘撰,能準(zhǔn)確地為業(yè)務(wù)建模。圖形化展示股票價(jià)格可能是另一個(gè)例子万皿,每一次價(jià)格的變動(dòng)都可認(rèn)為是一個(gè)事件摧找。
另一種顯然的用例是應(yīng)用需要同時(shí)處理大量I/O 操作。阻塞式I/O 需要同時(shí)使用大量線程牢硅,這會(huì)導(dǎo)致大量鎖之間的競(jìng)爭(zhēng)和太多的上下文切換蹬耘。如果想要處理成千上萬(wàn)的連接,非阻塞式I/O 通常是更好的選擇减余。
12.使用Lambda表達(dá)式編寫并發(fā)程序
使用基于Lambda 表達(dá)式的回調(diào)综苔,很容易實(shí)現(xiàn)事件驅(qū)動(dòng)架構(gòu)。
A.CompletableFuture 代表了 IOU位岔,使用 Lambda表達(dá)式能方便地組合如筛、合并。
B.Observable 繼承了 CompletableFuture 的概念抒抬,用來(lái)處理數(shù)據(jù)流杨刨。
13.下一步計(jì)劃
A. 向其他程序員(朋友或同事)解釋什么是Lambda ?? 表達(dá)式,為什么會(huì)對(duì)它產(chǎn)生興趣擦剑。
B.嘗試將目前從事的項(xiàng)目部署到 Java 8 環(huán)境下妖胀。如果現(xiàn)有單元測(cè)試已經(jīng)能運(yùn)行在持續(xù)集成系統(tǒng)Jenkins 下芥颈,那么在多個(gè)版本的Java 上構(gòu)建程序也易如反掌。
C. 使用新的 Stream 和 Collector赚抡,開始重構(gòu)真實(shí)產(chǎn)品中的遺留代碼爬坑。它既可以是感興趣的開放源碼項(xiàng)目,也可以是當(dāng)前從事的項(xiàng)目怕品,前提是第一步里已經(jīng)部署成功一個(gè)測(cè)試環(huán)境妇垢。
D.如果還沒準(zhǔn)備好大規(guī)模遷往Java 8,那么在分支上使用Java 8 做一些原型會(huì)是個(gè)不錯(cuò)的開始肉康。
E.有沒有一些大規(guī)模處理數(shù)據(jù)的代碼?或者代碼中存在并發(fā)問題灼舍?試著使用 Stream 處理數(shù)據(jù)吼和,或使用RxJava 中新的并發(fā)特性,也可以使用CompletableFuture 類骑素,來(lái)重構(gòu)你的代碼炫乓。
F.選擇一個(gè)熟悉的代碼庫(kù),分析它的設(shè)計(jì)和架構(gòu)献丑。
從宏觀上看末捣,有沒有更好的實(shí)現(xiàn)方法?
能否簡(jiǎn)化設(shè)計(jì)创橄?
能否減少實(shí)現(xiàn)某功能所需的代碼量箩做?
怎樣讓代碼更易讀?