Java為什么還在變化卸察?
Java從出生到現(xiàn)在已經(jīng)有近30年的時間了既忆,為什么它還需要不斷的變化呢?首先Java也不是盡善盡美的邑雅,畢竟每種語言都只是在整個編程的生態(tài)系統(tǒng)中占據(jù)某個位置而已片橡,此外硬件條件、程序的規(guī)模淮野、數(shù)據(jù)的量級等等都是逐漸變化的捧书。Java語言也需要去主動擁抱這些變化,綜合起來可以把Java為什么還在變化的原因總結為一下三點:
- 我們在之前的Java版本中要充分利用現(xiàn)代CPU多核心的優(yōu)勢骤星,就需要編寫多線程代碼经瓷,而多線程代碼不僅編寫復雜還容易出錯;
- 函數(shù)式比命令式更適應新的體系架構洞难;
- 什么是函數(shù)式編程:函數(shù)式編程指的是使用這種方式編寫的代碼舆吮,它的語體是
表達式
形式的,主要是用來計算的表達式(不是編程語言中的Function/Method的概念) - 什么是命令式編程:命令式編程指的是使用這種方式編寫的代碼,它的語體是面向?qū)ο缶幊讨谐R?guī)的語句形式歪泳;
- 為什么需要函數(shù)式編程:后面會細說
- 什么是函數(shù)式編程:函數(shù)式編程指的是使用這種方式編寫的代碼舆吮,它的語體是
- 相比其他語言萝勤,實現(xiàn)相同功能的代碼量一直是Java被人詬病的地方;
Java8引入的核心思想
Java8作為自Java5以來變化最大的一個版本呐伞,它引入的核心思想主要就是為了把計算
從面向?qū)ο蟮捏w系里獨立出來敌卓。隨著現(xiàn)在的業(yè)務邏輯越來越復雜,單純面向?qū)ο蟮木幊谭绞皆谔幚眍l繁多變的計算類業(yè)務邏輯時伶氢,顯得越來越笨拙趟径。而一些函數(shù)式編程語言在這方面卻大獲成功,這給與了Java的設計者相當大的生存壓力癣防。面向?qū)ο笏枷氲膹姶笾幵谟谖锨桑芎茏匀坏膶φ鎸嵤澜缫粋€個實體的屬性和行為進行抽象。而計算更多的是發(fā)生在對象之間蕾盯,它像是一個沒有固定形態(tài)的工具幕屹。所以把函數(shù)式編程思想引入到Java中來,作為面向?qū)ο笏枷氲囊粋€補充是很有必要的级遭。
Java8中引入函數(shù)式編程思想的方式非常的巧妙望拖,可以總結為一下兩點:
- 將方法和Lambda作為一等公民——像基本數(shù)據(jù)類型和對象引用一樣的能在程序運行時傳遞;
- 在沒有可變共享狀態(tài)時挫鸽,函數(shù)或方法可以有效说敏、安全地并行執(zhí)行——通過流API在底層進行實現(xiàn);
Java8帶來了哪些新特性丢郊?
以下新特性部分先寫個知識點的大綱盔沫,后續(xù)再分章節(jié)逐一細說。
Stream API
Streams是Java8的核心內(nèi)容枫匾,另外的兩項特性(方法引用/Lambda架诞、接口默認函數(shù))主要就是用來實現(xiàn)Streams(流處理);
為什么需要流干茉?
- 現(xiàn)有的對集合的處理代碼谴忧,顯得很繁瑣(嵌套的循環(huán)、條件判斷等)等脂;
- 不能利用多核俏蛮;
StreamAPI的設計思想
- 讓集合保留他存儲和訪問數(shù)據(jù)的功能,而使用Stream來對數(shù)據(jù)進行計算上遥;
向方法中傳遞代碼
Java語言中的一等公民
- 編程語言的整個目的就在于操作值搏屑,在Java8之前,這個值包括基本數(shù)據(jù)類型和對象的引用粉楚;
- 在Java中還有方法和類辣恋,在執(zhí)行期間是不能被傳遞的亮垫,所以是二等公民;
- Java 8中新增了函數(shù)——值的一種新形式伟骨;
在JavaScript中饮潦,類也是可以傳遞的。
Scala和Groovy等語言的實踐已經(jīng)證明携狭,讓方法等概念作為一等值可以擴充程序員的工具庫继蜡,
從而讓編程變得更容易。
方法引用的寫法
Java8之前的寫法
File[] files = new File(path).listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isHidden();
}
});
Java8中的寫法
File[] files = new File(path).listFiles(File::isHidden);
最大的好處是:我們的代碼現(xiàn)在讀起來更接近問題的陳述了逛腿。
匿名函數(shù)-Lambda
除了允許(命名)函數(shù)成為一等值外稀并,Java 8還體現(xiàn)了更廣義的將函數(shù)作為值的思想,包括Lambda(匿名函數(shù))单默。
-
對于已有的方法碘举,可以很方便的使用方法引用的寫法,但是如果是一段即時代碼(比如一個計算公式)搁廓,直接用匿名函數(shù)的形式傳入方法中引颈,會更簡潔。否則需要定義一個類境蜕、一個方法蝙场,把這個計算公式放到方法中。
new File(path).listFiles(file -> file.isHidden());
listFiles()方法中需要的參數(shù)是一個FileFilter汽摹,為什么可以傳入一個Lambda表達式
接口中的默認方法
為什么要在接口中增加默認方法李丰?
- Java的設計者發(fā)現(xiàn)JDK中的接口也需要修改苦锨,但如果直接修改接口的話逼泣,意味著接口的實現(xiàn)者都需要跟著修改,這是毀滅性的災難舟舒。所以設計了接口的默認方法拉庶,所有的實現(xiàn)類都可以使用這些默認接口,而不需要修改自身秃励。
Optional
為了更好的解決和避免NPE異常氏仗,Java 8中引入了一個新的類java.util.Optional。
這是一個封裝Optional值的類夺鲜。
舉例來說皆尔,使用新的類意味著,如果你知道一個人可能有也可能沒有車币励,那么Person類內(nèi)部的car變量就不應該聲明為Car慷蠕,遭遇某人沒有車時把null引用賦值給它,而是將其聲明為Optional類型食呻。
變量存在時流炕,Optional類只是對類簡單封裝澎现。變量不存在時,缺失的值會被建模成一個“空”的Optional對象每辟,由方法Optional.empty()返回剑辫。Optional.empty()方法是一個靜態(tài)工廠方法,它返回Optional類的特定單一實例渠欺。
null引用和Optional.empty()有什么本質(zhì)的區(qū)別嗎妹蔽?
使用Optional而不是null的一個非常重要而又實際的語義區(qū)別是,第一個例子中挠将,我們在聲明變量時使用的是Optional類型讹开,而不是Car類型,這句聲明非常清楚地表明了這里發(fā)生變量缺失是允許的捐名。與此相反旦万,使用Car這樣的類型,可能將變量賦值為null镶蹋,這意味著你需要獨立面對這些成艘,你只能依賴你對業(yè)務模型的理解,判斷一個null是否屬于該變量的有效范疇贺归。