java8

ch1 java概覽

1.java歷史和新特性

java自從1998年發佈版本1.0後經歷了很多次新版本的更新,其中具有里程碑意義的版本是jdk5和jdk8,其中jdk5引入了泛型、增強for循環等對如今開發有着深遠影響的特性,隨着時代的進步,動態語言中的函數式編程、類型推斷等特性越來越流行,爲了不被其他動態語言所替代,java必須跟隨時代的腳步升級,jdk8便是時代的產物,它首次嘗試在靜態語言中引入動態語言的特性,可以預見java8將會對未來的java開發產生深遠的影響,此次java包含了lambda表達式、stream、optional、默認方法、新日期與時間api等特性。

流處理Stream

java8主要新特性之一,它的思想是將數據看做流,對每個數據依次進行一直多箇中間操作,最後生成想要的結果,是對java8以前集合的一種升級,類似於linux中管道命令一樣,比如 cat file1.txt | tr | sort | tail 命令,每個命令的輸出都是後一個命令的輸入,直至最後一個命令產生結果。

行爲參數化

java8中引入的一個重要概念,以前java只接受將普通值或對象作爲參數傳遞給方法,現在可以將一個方法作爲參數傳遞給另一個方法,被當做參數的方法也被稱爲行爲,在動態語言中稱爲函數,函數是動態語言中的一等公民,不需要依託任何類即可存在。

並行

java8以前想要實現並行必須要自己寫業務邏輯,手動開啓多線程,現在依賴於Stream可以很方便的將執行方法並行化,用戶會將stream中每個節點需要做的事情以函數的方式傳遞過來,stream內部可以通過機制決定開啓幾個線程同時執行此函數。

2.函數

java中的值

此處的值指的是傳遞給方法的參數,可以是普通值,比如1.2、"abc"等,也可以是個對象引用,比如ArrayList list, 也可以是匿名函數,本質上也是一個對象。

方法引用作爲值

通過類名.靜態方法名的方式將方法作爲值傳遞給方法,java8中的寫法爲File::isHidden,這是java8引入的一種新寫法,表達的意思是調用File類中的靜態方法isHidden。

lambda(匿名函數)做爲值

java8中新引入的lambda本質上就是一個簡寫的匿名函數,java8提供了一種編寫匿名函數更簡單的寫法,這種寫法就叫做lambda,因爲匿名函數是java中的值,可以作爲參數傳遞給方法,所以lambda也可以作爲參數傳遞給方法。

例子

普通值和對象引用作爲參數進行傳遞是每個學習java語言的人必會的技能,此處主要看下方法引用和lambda的寫法,需求假設需要查找某個目錄下所有隱藏的文件

首先是普通的寫法:

在這裏插入圖片描述

接着是lambda的寫法:

在這裏插入圖片描述

最後是方法引用的寫法:

在這裏插入圖片描述

可以看到在此例子中方法引用其實是lambda的一種簡寫形式。

3.流

外部迭代和內部迭代

用戶可以看到的迭代稱爲外部迭代,比如使用foreach循環遍歷集合,在對每個對象進行操作;用戶看不到的迭代稱爲內部迭代,比如stream中的方法,你只需要指定方法即可,stream內部會幫你完成循環並按照你指定的方法處理每個元素。

流對並行的支持

因爲stream使用內部迭代的方式遍歷元素,因此該如何使用並行提高元素迭代效率在stream內部可以做到完全控制,如果是外部迭代則只能自己在循環中開啓並分配線程,難度極大。

4.默認方法

默認方法出現的原因

java8新增了stream的概念,stream是對集合的一種增強,創建流的其中一個方式是list.stream(),但是這種方式存在一個問題,原先的設計中並沒有考慮到後續版本會使用此方法,因此並沒有提供該方法,現在java8爲了讓所有的集合都可以使用此方法,則必須將該方法加入到List接口中,這樣做會導致所有實現了java集合的類都必須實現此方法,這種方式並不可以保持兼容性,因此提出了默認方法的概念,即通過default關鍵字在接口中實現該方法,不需要實現類做任何改動便可以使用stream方法。

5.函數式編程的新思想

Optional

java以前的版本存在一個很重大的設計失誤,該設計因爲使用方便而沒有修正,此設計是null,即經常遇到的NPE問題,java8決定修正該失誤,因此它提供了Optional類,該對象會包含所有的值,包括普通值、對象引用等等,你可以通過調用它的get方法獲取包含的值,如果此值爲空可以強迫用戶處理值不存在的問題,不會出現NPE問題了。

模式匹配

模式匹配可以看做是switch的一種擴展形式,是動態語言中特有的概念,對java的設計產生了一些影響。


ch2 行爲參數化

本章主要講了行爲參數化的概念以及如何使用它,將篩選蘋果的需求作爲例子進行實踐

1.行爲參數化的含義和使用

首先要搞清楚行爲的含義,簡單來說行爲=方法=函數,行爲參數化指的就是將一個方法或函數作爲參數傳遞給另一個方法,假設現在要根據顏色篩選蘋果,首先假設需要找出所有綠蘋果:

在這裏插入圖片描述

通過調用此方法,指定color等於blue即可實現,此時需求變了,要求篩選出所有紅色的蘋果,那麼你需要將color改變爲red來完成,到目前爲止一切開起來都很美好,此時需求又變了,要求篩選出所有重量大於100的蘋果,此時用此方法就沒辦法實現了,必須重新寫一個實現方法:

在這裏插入圖片描述

通過調用此方法,指定weight爲100即可實現,假設現在需求又又又又變了,需要篩選所有產地爲北京的蘋果,用戶當前可以在寫一個方法,通過參數的方式將產地傳入,假設需求想要即是紅色由大於100的蘋果怎麼辦,總不可能在寫一個方法吧,爲了解決這個問題,java8引入了行爲參數化的概念。

通過上面的方法可以發現需求中總是在變的只有輸入的參數和具體的判斷邏輯不同,因此我們可以將判斷邏輯抽象爲一個方法,該方法存在於接口中,參數即爲遍歷中的對象類型,具體的判斷邏輯由實現類編寫,抽象以後的代碼就會變爲這樣:

在這裏插入圖片描述

每個條件被抽象爲了一個類,這些類實現了同一個接口,此時用於過濾的方法便可寫爲:

在這裏插入圖片描述

此方法寫好以後可以進行復用,比如此時要實現篩選顏色爲red且重量大於100的蘋果,只需創建一個類實現接口中的方法,在該方法中編寫具體的判斷邏輯即可,篩選方法完全不需要做改變,具體實現類中的test方法即稱爲一種行爲,我們通過將行爲包裝在接口中傳遞給方法從而實現了行爲的參數化。

2.和策略模式的聯繫

上面這種方式和策略模式非常相似,篩選方法只要面向ApplePredicate接口即可,我們可以實現多個該接口的實現類,具體使用哪個依賴於所傳入的具體實現類,實際上這既是策略模式的一種實現。

3.實現自定義的行爲參數化例子

假設現在需要按照不同格式打印每個蘋果的屬性,重新設計一個接口:

在這裏插入圖片描述

可以通過實現該接口定義不同的打印方式,使用該接口的方式同filterApple一樣,只需要將接口和調用的方法換掉即可。

4.減少代碼量,使用匿名類重寫

到目前爲止功能已經實現了,但是爲每一個需求編寫一個實現類然後將它傳入到方法中太麻煩了,我們可以使用java提供的匿名類減少代碼量:

在這裏插入圖片描述

可以看到使用匿名類的方式調用我們不必在編寫實現類了,代碼簡化了很多。

5.再進一步,使用Lambda重寫

使用匿名類優點過於笨重,而且不夠友好,因此java8提供了lambda取代匿名類,將上面的例子使用lambda重寫:
在這裏插入圖片描述

可以看到使用lambda相比之前的代碼簡化了非常多。

6.繼續抽象

如果將上面的filterApple方法和接口繼續抽象,將Apple替換爲泛型T,則此套路可以用作任何類型的篩選,此接口和方法即是java8內置的filter方法和Predicate方法,實現原理同上面例子一樣。


ch3 lambda

1.lambda是什麼和它的作用

通過前面的介紹可以很清楚的知道lambda實際上是匿名函數的一種簡寫形式,使用lambda可以使得代碼變得更加精簡,它非常適合用來表示 ‘將行爲傳遞給方法參數’ 這個概念。

2.lambda的組成

既然lambda本質上是一個匿名函數,那麼它的定義和普通方法類似,lambda主要由參數列表,箭頭符號,方法體組成

具體表示爲(parameter) -> expression 或者 (parameter) -> {statement;}

如果不帶花括號一定是一個表達式不可以是語句,因爲lambda默認會將最後執行的代碼的結果作爲返回值返回(借鑑scala語言的思想)。

3.lambda的使用位置

我們可以使用匿名函數是因爲在接口中定義了那個方法,如果接口中沒有對應的方法,那麼匿名函數也就不會存在,同樣的道理,現在lanmbda代表了匿名函數,那麼一定有一個接口聲明瞭lambda代表的抽象函數,就像在前面抽象出來的ApplePredicate接口一樣,只有在接口中聲明瞭test方法,我們纔可以使用lambda的方式爲test方法編寫實現行爲

函數式接口

爲了區分普通接口和lambda所使用的接口,java8引入了一個新註解@FunctionalInterface,被該註解標記的接口稱爲函數式接口,如果是函數式接口,那麼就應該滿足指定的規則:接口中只有一個抽象方法(沒有參數或通過繼承導致存在多個方法的接口都不符合要求)。

函數描述符

和方法描述符類似,因爲lambda沒有名字,爲了更清楚的區分各個函數式接口中的抽象方法,爲lambda定義一種描述符,比如()-> {} lambda表達式可以表示爲 ()-> void,即代表其不接受任何參數,不返回任何值,前面定義的ApplePredicate接口中的test方法的lambda實現可表示爲(Apple)-> boolean,即接收一個Apple對象,返回一個boolean值

4.lambda使用示例

以處理文件爲例

初始實現:

在這裏插入圖片描述

此時讀取一行沒問題,如果想要讀取兩行呢,三行呢,每行中間加數字呢,總不可能每次都寫一大堆重複的代碼吧,此時就要使用lambda把變化的行爲抽取出來作爲參數傳遞

首先確認參數,在這個方法裏我們只需要別人傳入br對象引用,然後調用readLine方法即可,因此參數即爲BufferedReader br

其次確定返回值,此方法需要返回一個String對象,由此可以得到lambda的函數描述符:(BufferedReader)-> String

函數實現已經確定了,接着就要把它和函數式接口關聯起來,只有在使用該函數式接口作爲參數的地方纔可以將此lambda的實現作爲參數傳入方法

定義一個函數式接口,起名爲BufferedReaderInterface,其中的抽象方法爲processFile

在這裏插入圖片描述

接着編寫該lambda的通用方法,在此方法中將具體行爲轉交給lambda表達式實現

在這裏插入圖片描述

接着就可以給這個通用方法傳遞任意符合類型的lambda表達式了,具體實現行爲由自己決定

在這裏插入圖片描述

此處的br對象是由通用方法傳遞過來的。

5.java8自帶的函數式接口

通過前面可以看出如果要使用lambda表達式則必須先定義函數式接口,聲明唯一的抽象方法才行,爲了方便java開發人員編寫lambda,設計者們抽象出來多個行爲,並定義好了這些行爲的函數式接口,因此只要是以這些接口作爲參數的地方就可以直接編寫lambda表達式了,這些接口稱爲java內置的函數式接口

Predicate

此接口中方法對應的函數描述符爲T -> boolean, 正如前面的例子中傳入一個Apple對象返回結果爲true或者false

Consumer

此接口就像消費者一樣,它的函數描述符爲T -> void, 即傳入T類型對象,不返回值,傳入的對象被它消費了

Function<T, R>

此接口和數學中的函數類似,它的函數描述符爲 T -> R, 即傳入T類型對象,返回R類型對象

原始類型和引用類型的函數式接口

這種類型的函數式接口本質上和前三種一樣,唯一的區別在於它們的類型爲引用類型或原始類型,比如IntPredicate代表傳入int值,返回boolean值,其他的和此類似,提供這些接口的目的是因爲使用這些接口可以避免拆箱和裝箱操作

java提供的所有函數式接口列表

在這裏插入圖片描述

在這裏插入圖片描述

如果java提供的不能滿足需求,那麼可以自己重新實現

6.類型檢查、推斷和限制

首先要明確一點,討論lambda是在特定的上下文中討論的,因爲一個lambda表達式可以對應多個函數式接口,只有先確定了函數式接口,纔有類型檢查和推斷的問題

使用ApplePredicate舉例來說,該接口中唯一的抽象方法需要的參數爲Apple對象,如果對應的lambda表達式的參數不是Apple,那麼java就會拋出異常

類型推斷指的是在lambda中你不需要顯示的聲明參數的類型,因爲lambda只可能對應一個函數式接口中的一個抽象方法,因此當你不寫參數類型時java會根據抽象方法的參數類型確定lambda參數的類型

lambda中的限制和匿名函數一樣,即函數中只能使用被final修飾的外部變量

7.方法引用

方法引用的含義、寫法和作用

方法引用是java8新提出來的概念,目的是簡化lambda的代碼,它使用 類名或對象引用名::方法或關鍵字 表示,它只適合簡化那種函數體內只有一行代碼,並且是調用其他方法的lambda,比如(Apple a) -> a.getWeight() 可以簡寫爲 Apple::getWeight

方法引用的三種分類

分爲靜態方法引用, 寫法爲 類名::靜態方法名

參數類型實例方法引用, 寫法爲 參數類型所對應的類::參數類型所對應的類中的實例方法

對象實例方法引用, 寫法爲 變量名::變量對應的對象中的實例方法名

用一幅圖總結:

在這裏插入圖片描述

構造函數引用

寫法爲 類名::new,它返回的是類名所對應的新對象,對應函數式接口的函數描述符爲()-> T, 對應java提供的Supplier函數接口

8.表達式複用

在java提供的函數式接口中除了提供一個抽象方法外還提供了多個默認方法,這些方法的作用就是複用表達式,比如and方法,接收一個Predicate返回一個Predicate,那麼只有在兩個lambda都爲true時纔會返回true,還有andThen和compose,假設調用關係是f.andThen(g)和f.compose(g),則類似於數學中的g(f(x))和f(g(x)),即哪個先執行哪個後執行的問題


ch4 流的介紹

1.流帶來的好處

流可以使開發者更方便的處理集合,它可以看做集合的一種增強,使用了lambda中行爲參數化的思想,將對集合中元素的行爲抽取爲各種類型的抽象方法,底層類庫只需要編寫通用的實現,具體對集合中元素的處理操作由lambda表達式決定,因此流的出現改變了以前只能以命令的方式操作集合,使用流可以通過聲明式編程操作集合,因爲通用的方法由jdk底層提供,與用戶完全解耦,因此jdk可以很方便的在處理流時使用並行化

2.流的定義

標準定義是:從支持數據處理操作的源生成的元素序列

由定義可知流是由多個元素組成的元素序列,而元素是來自於各種可以處理數據的容器,比如ArrayList、數組對象等等,每一種容器java8都爲其提供了轉換爲流的默認方法

流在處理數據時具有流水線和內部迭代的特性

具體使用示例如下:

首先定義Dish類
在這裏插入圖片描述

構造數據

在這裏插入圖片描述
在這裏插入圖片描述

這個方法做的操作是獲取食物列表中卡路里大於300的食物,只取前三條記錄的名字,如果使用傳統的方式實現需要很多行代碼,由此可以看到流對集合能力的擴展效果

3.流的特性

遍歷一次

將集合或容器轉爲流以後只能使用一次,就像menu.stream() 一樣,調用該方法會生成一個代表流的Steam對象,當collect方法執行完畢後該對象便消失了,如果想重新使用此流對象,則必須重新調用menu.stream() 方法,因此說流只能遍歷一次

外部迭代和內部迭代

外部迭代指的是用戶可以看到迭代語句的迭代過程,內部迭代指的是用戶看不到迭代語句的迭代過程

比如使用Iterator遍歷時就是外部迭代,因爲必須手動調用hasNext方法進行判斷,而foreach語句也是外部迭代

stream爲內部迭代,因爲用戶只需要指定流中每個元素需要執行的操作,Stream內部會挨個遍歷元素並執行相應的操作,對用戶完全透明

在這裏插入圖片描述

4.流定義的操作

流內置了很多方法(操作),根據對元素序列的影響主要分爲中間操作和終端操作

中間操作

這類方法不會導致流開始收集元素,流只有在遇到終端操作時纔會開始執行方法並生成結果,中間方法具有延遲特性,每個中間操作返回的都是Stream對象

終端操作

顧名思義,終端操作就是使流結束的方法,當指定終端方法後流就會開始遍歷元素序列,依次執行中間操作,最後通過終端操作返回計算後的結果,之後該流對象便會被回收

使用流

有上面可知,流的使用有三部分組成:數據源、中間操作、終端操作,流的這種使用方式類似於構建器模式


ch5 流的用法

和lambda的函數式接口類似,java也爲通用的集合處理操作封裝了一系列方法,只有在詳細理解了這些方法的作用之後纔可以正確的使用它們

1.中間操作方法之篩選和切片

filter

filter方法的作用是過濾數據,它的參數是Predicate<? super T> predicate,函數描述符爲 T -> boolean,大概的實現流程爲Stream遍歷元素,調用每個元素的predicate,該參數是由用戶指定的lambda表達式,如果返回結果爲true,則將其加入下一個處理流中,如果爲false,則過濾掉該元素
在這裏插入圖片描述

執行流程爲:
在這裏插入圖片描述

distinct

distinct方法的作用是去除流中重複的元素,它的判斷依據是hashcode和equals方法

在這裏插入圖片描述

在這裏插入圖片描述

limit

limit方法的作用是獲取前n個元素,其中n由用戶指定

在這裏插入圖片描述
在這裏插入圖片描述

skip

skip方法的作用是跳過指定個數的元素

在這裏插入圖片描述

在這裏插入圖片描述

2.中間操作方法之映射

map

map方法的作用是將元素映射爲另一種類型,就像前面獲取菜名一樣,本來集合中存放的是Dish類型對象,但是最後生成的卻變成了String類型對象,因爲給map中傳入了Dish::getName

flatmap

flatmap方法和map類似,只不過它是在映射的同時將多個流壓成一個流輸出,即首先通過map方法向值映射爲流,然後將多個流輸出到同一個流中返回

3.終端操作方法之查找和匹配

anyMatch

anyMatch方法接收的參數和filter方法一樣,它的作用是在經過中間操作後的流中應用指定的lambda行爲,只要流中有一個滿足條件則返回true

allMatch

allMatch方法和anyMatch方法類似,只有流中所有的元素都滿足指定條件後才返回true

noneMatch

noneMatch方法可以看做allMatch方法的反義詞,即只有流中所有的元素都不滿足指定條件時返回true

findAny

findAny方法的作用是在流中隨意找一個元素返回, 該返回類型爲Optional,即有可能找到也有可能找不到,避免空指針異常

findFirst

findFirst方法查找流中的第一個元素並返回,返回類型爲Optional

4.終端操作方法之求和

reduce

reduce方法的作用是對元素進行一些歸約操作,歸約操作的含義是指在流生成的元素上執行一些查詢或操作,然後返回執行後的結果

首先介紹它兩個參數的方法,第一個參數爲初始值,第二個參數爲BinaryOperator類型的lambda表達式,它的大概含義是依次遍歷元素,以初始值作爲基礎,將每個元素和初始值傳入lambda中,然後將計算的結果作爲下一次lambda的參數,最後返回計算結果

使用reduce進行累計求和的執行流程如下

在這裏插入圖片描述

reduce還有一個只接受一個參數的方法,該方法的返回值爲Optional,使用該方法還可以實現最大值和最小值的計算

無狀態和有狀態

無狀態指操作不需要依賴歷史生成的結果即可進行,比如map和filter

有狀態指操作必須依賴歷史生成的結果纔可進行,比如sort和count

目前爲止接觸到的所有stream方法

在這裏插入圖片描述

5.數值流

出現的意義

爲了避免流中每次拆箱和裝箱的性能損耗,java定義了專門用於處理普通類型的流

特化流

該流的方法和普通流中的方法類似,只是在每個方法後添加To基本類型

特化流轉爲普通流

需要轉換時只要調用boxed方法即可

6.生成流的方式

從前面已經瞭解了流的使用方式和常見的一些方法,本節主要講解如何創建流,流本質上是一個個元素組成的元素序列,因此只要對象的內容滿足此定義就可以通過方法轉爲流,java8提供了5種流的來源

集合

在java8中爲集合接口添加了一個stream的默認方法,通過此方法可以將集合中的元素轉爲流

最典型的應用是通過Arrays的靜態方法asList指定多個元素生成集合,然後調用集合的stream方法

數組

java8給Arrays提供了stream方法

文件

Files中的靜態方法list、lines等都可以將文件轉爲流

函數

調用Stream的靜態方法iterate可以生成無限流


ch6 收集數據

1.收集器的作用

收集器collect可以看做是reduce的一種擴展,使用reduce時只能做求最大值、最小值、計算總和等簡單的數據操作,而collect支持分區、分組、排序和各種彙總函數等功能

2.歸約和彙總

歸約和彙總函數的種類和使用方式

java8提供的collect功能放在了Collectors類裏,比如toList、toMap、groupingBy等方法,這些方法大致可以分爲三類:將流元素歸約和彙總爲一個值、元素分區、元素分組

基本使用方式爲:集合容器.stream().多箇中間方法.collect(Collectors.指定的函數)

比如求最大值的方法maxBy,它會接受一個比較器,最後返回集合中指定值最大的那個對象

在這裏插入圖片描述

其他的方法都是類似的,唯一的不同就是它們的參數根據方法的定義而改變

歸約和彙總函數的本質實現

上面介紹的Collectors中的各種歸約方法其實都是Collectors.reducing()方法的特殊實現,只是用來方便使用的,底層都是通過reducing實現的

3.分組

顧名思義,就是將流中的數據按照指定的方式分成多組,最後返回分組後的map集合,它允許進行多級分組和統計

4.分區

和分組的區別

如果分組的參數改爲Predicate,那麼此時就是分區了,因此分區爲接受一個謂詞,將流中結果爲true的分爲一組,爲false的分爲另一組,最後返回的也是一個map,它也允許進行多級分區

Collectors方法列表

在這裏插入圖片描述

在這裏插入圖片描述

5.Collector

Collector是一個接口,其中有五個方法,它是所有歸約和彙總方法的抽象,通過實現它的方法可以定義自己的收集器,Collectors中通過靜態內部類的方式實現該接口

函數介紹

實現toList方法

6.自定義收集器

需要先理解Collector接口中每個方法的作用纔可以自定義收集器


ch7 在流中使用並行

1.並行流

將順序流轉爲並行流的方式

因爲流使用的內部迭代的方式,因此在流中使用並行非常簡單,只需要將轉爲流的方法stream替換爲parallelStream即可,或者調用parallel方法

順序流和並行流性能測試

並不是說使用並行的方式一定比串行快,有時因爲任務難以分割、裝箱和拆箱等因素會導致並行比串行還慢,因此在正確的地方使用並行是很有必要的

2.fork/join框架

fork、join原理

如同名字一樣,該框架首先將任務fork,即將任務拆分成多個最小單元,然後使用並行執行各個單元,最後將執行結果通過join收集起來,充分利用多處理器的優勢提高運算速度

在這裏插入圖片描述

工作竊取

在將任務分爲多個小任務後,由於每個處理器處理速度可能不同,就會出現1號處理器已經處理完任務了,2號、3號、4號處理器還有任務沒有處理完成,此時1號處理器就會幫助任務堆積最多個處理器共同處理任務,爲了不發生衝突,它會從任務隊列的尾部獲取任務,這種方式就像偷走任務一樣,因此稱爲工作竊取

3.流拆分機制Spliterator

作用

當使用並行方式時指定流該如何切分任務,比如流中序號爲奇數的分爲一組、序號爲偶數的分爲一組等等,而Spliterator就是java對這些拆分機制的一種抽象

接口方法介紹

接口中最重要的方法即爲 trySplit(),它定義了具體的拆分規則,它返回的還是Spliterator,因此可以繼續調用trySplit方法進行切分直至返回null,此時代表已經不可再分

在這裏插入圖片描述

自定義拆分類

只需要實現Spliterator接口,實現其中的方法即可,它除了由trySplit之外,還提供了一些輔助方法


ch8 重構、測試和調試

在這裏插入圖片描述


ch9 默認方法

1.默認方法出現的原因

思考一個很實際的問題,比如將ArrayList轉換爲Stream的方式爲調用stream方法,但是在java8以前集合中並沒有定義stream方法,如果將該方法加入集合類中,那麼用戶實現的所有集合類都要重新實現stream方法, 這導致java8不具備向前兼容的特性,此時默認方法就出現了,它允許在接口中實現方法,通過將方法標記爲default即可,這樣接口的實現類不需要做任何改動就可以使用該方法了

2.默認方法的定義

默認方法和普通方法有着同樣的定義,唯一的區別是它的標識符爲default,並且只能寫在接口裏

3.默認方法帶來的問題及解決方式

如果在多個接口中定義了同名的默認方法,比如A接口定義了stream默認方法,B接口也定義了stream方法,同時它繼承A接口,此時C類實現了A、B接口並調用stream方法,那麼根據就近原則它調用的是B接口的stream方法,如果嚮明確的調用A接口中的stream方法,可以使用A.super.stream的方式調用

如果出現了C++中菱形繼承的調用方式,那麼編譯器會報錯,你必須通過A.super.stream的方式手動指定調用哪個接口中的方法


ch10 Optional

1.Optional出現的原因

Optional類最主要的作用就是用來避免空指針異常,在java8中當返回對象時不是直接返回對象,而是返回一個Optional類,該類中包含了返回的對象,這樣做的好處是如果用戶需要獲取返回的對象,那麼必須手動的調用方法進行獲取,此時就必須考慮該值爲空的情況

在這裏插入圖片描述

2.Optional類

在這裏插入圖片描述

在這裏插入圖片描述

3.創建Optional對象的方式

在這裏插入圖片描述

使用Optional的方式是如果某個值可能爲空,那麼使用ofNullalbe的方式將它包裝成Optional,比如在map中根據鍵獲取值時: Optional.ofNullable(map.get(key))


ch11 CompletableFuture

1.Future接口

Future代表了任務執行時將來會生成的結果,executorService執行後會返回該對象,通過該接口可以實現異步的操作,只有在需要獲取任務執行的結果時調用get方法纔會阻塞當前線程

在這裏插入圖片描述

2.CompletableFuture

completableFuture是java8對Future的一種增強,它使用了java8中的新概念提供了比Future更多的特性,包括異常管理機制、將多個異步任務合併爲一個等等

3.completion事件

在completableFuture中註冊了該事件後,當任務執行完畢或者結果可用時會回調註冊的方法,由此用戶可以不需要等待直接獲取到執行結果


ch12 日期和時間

java8提供了一套全新的日期和時間API,解決了以前版本中遺留的問題

1.日期和時間間隔

LocalDate、LocalTime、LocalDateTime

在這裏插入圖片描述

Duration、Period

在這裏插入圖片描述

通用方法

表示日期或時間間隔

在這裏插入圖片描述

表示日期或時間點

在這裏插入圖片描述

2.日期操作

TemporalAdjuster

主要用來獲取各種需求的日期,它是一個接口,如果提供的方法不能滿足要求,可以自己提供專門的實現類

在這裏插入圖片描述

3.時間操作

主要改動是通過ZoneId替換了TimeZone,可以處理不同的時區, 還提供了4中其他的日曆系統

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章