Java8學習小記

轉載自https://segmentfault.com/a/1190000006985405java

 

 

2014年,Oracle發佈了Java8新版本。對於Java來講,這顯然是一個具備里程碑意義的版本。尤爲是那函數式編程的功能,避開了Java那煩瑣的語法所帶來的麻煩。程序員

這能夠算是一篇Java8的學習筆記。將Java8一些常見的一些特性做了一個概要的筆記。數據庫

行爲參數化(Lambda以及方法引用)

爲了編寫可重用的方法,好比filter,你須要爲其指定一個參數,它可以精確地描述過濾條件。雖然Java專家們使用以前的版本也能達到一樣的目的(將過濾條件封裝成類的一個方法,傳遞該類的一個實例),但這種方案卻很難推廣,由於它一般很是臃腫,既難於編寫,也不易於維護。編程

Java 8經過借鑑函數式編程,提供了一種新的方式——經過向方法傳遞代碼片斷來解決這一問題。這種新的方法很是方便地提供了兩種變體。segmentfault

  • 傳遞一個Lambda表達式,即一段精簡的代碼片斷,好比
apple -> apple.getWeight() > 150
  • 傳遞一個方法引用,該方法引用指向了一個現有的方法,好比這樣的代碼:
Apple::isHeavy

這些值具備相似Function<T, R>、Predicate<T>或者BiFunction<T, U, R>這樣的類型,值的接收方能夠經過apply、test或其餘相似的方法執行這些方法。Lambda表達式自身是一個至關酷炫的概念,不過Java 8對它們的使用方式——將它們與全新的Stream API相結合,最終把它們推向了新一代Java的核心。設計模式

閉包
你可能已經據說過閉包(closure,不要和Clojure編程語言混淆)這個詞,你可能會想Lambda是否知足閉包的定義。用科學的說法來講,閉包就是一個函數的實例,且它能夠無限制地訪問那個函數的非本地變量。例如,閉包能夠做爲參數傳遞給另外一個函數。它也能夠訪問和修改其做用域以外的變量。如今,Java 8的Lambda和匿名類能夠作相似於閉包的事情:它們能夠做爲參數傳遞給方法,而且能夠訪問其做用域以外的變量。但有一個限制:它們不能修改定義Lambda的方法的局部變量的內容。這些變量必須是隱式最終的。能夠認爲Lambda是對值封閉,而不是對變量封閉。如前所述,這種限制存在的緣由在於局部變量保存在棧上,而且隱式表示它們僅限於其所在線程。若是容許捕獲可改變的局部變量,就會引起形成線程不安全的新的可能性,而這是咱們不想看到的(實例變量能夠,由於它們保存在堆中,而堆是在線程之間共享的)。數組

函數接口

Java 8以前,接口主要用於定義方法簽名,如今它們還能爲接口的使用者提供方法的默認實現,若是接口的設計者認爲接口中聲明的某個方法並不須要每個接口的用戶顯式地提供實現,他就能夠考慮在接口的方法聲明中爲其定義默認方法。緩存

對類庫的設計者而言,這是個偉大的新工具,緣由很簡單,它提供的能力能幫助類庫的設計者們定義新的操做,加強接口的能力,類庫的用戶們(即那些實現該接口的程序員們)不須要花費額外的精力從新實現該方法。所以,默認方法與庫的用戶也有關係,它們屏蔽了未來的變化對用戶的影響。第9章針對這一問題進行了更加深刻的探討。安全

在接口上添加註解:@FunctionalInterface。便可聲明該接口爲函數接口。數據結構

若是你去看看新的Java API,會發現函數式接口帶有@FunctionalInterface的標註。這個標註用於表示該接口會設計成一個函數式接口。若是你用@FunctionalInterface定義了一個接口,而它卻不是函數式接口的話,編譯器將返回一個提示緣由的錯誤。例如,錯誤消息多是「Multiple non-overriding abstract methods found in interface Foo」,代表存在多個抽象方法。請注意,@FunctionalInterface不是必需的,但對於爲此設計的接口而言,使用它是比較好的作法。它就像是@Override標註表示方法被重寫了。

Lambdas及函數式接口的例子:

使用案例 Lambda例子 對應的函數式接口
布爾表達式 (List<String> list) -> list.isEmpty() Predicate<List<String>>
建立對象 () -> new Apple(10) Supplier<Apple>
消費一個對象 (Apple a) ->System.out.println(a.getWeight()) Consumer<Apple>
從一個對象中選擇/提取 (String s) -> s.length() Function<String, Integer>或ToIntFunction<String>
合併兩個值 (int a, int b) -> a * b IntBinaryOperator
比較兩個對象 (Apple a1, Apple a2) ->a1.getWeight().compareTo(a2.getWeight()) Comparator<Apple>或BiFunction<Apple, Apple, Integer>或ToIntBiFunction<Apple, Apple>

簡介

要討論流,咱們先來談談集合,這是最容易上手的方式了。Java 8中的集合支持一個新的stream方法,它會返回一個流(接口定義在java.util.stream.Stream裏)。你在後面會看到,還有不少其餘的方法能夠獲得流,好比利用數值範圍或從I/O資源生成流元素。

那麼,流究竟是什麼呢?簡短的定義就是「從支持數據處理操做的源生成的元素序列」。讓咱們一步步剖析這個定義。

  • 元素序列——就像集合同樣,流也提供了一個接口,能夠訪問特定元素類型的一組有序值。由於集合是數據結構,因此它的主要目的是以特定的時間/空間複雜度存儲和訪問元素(如ArrayList 與 LinkedList)。但流的目的在於表達計算,好比你前面見到的filter、sorted和map。集合講的是數據,流講的是計算。咱們會在後面幾節中詳細解釋這個思想。
  • 源——流會使用一個提供數據的源,如集合、數組或輸入/輸出資源。 請注意,從有序集合生成流時會保留原有的順序。由列表生成的流,其元素順序與列表一致。
  • 數據處理操做——流的數據處理功能支持相似於數據庫的操做,以及函數式編程語言中的經常使用操做,如filter、map、reduce、find、match、sort等。流操做能夠順序執行,也可並行執行。

此外,流操做有兩個重要的特色。

  • 流水線——不少流操做自己會返回一個流,這樣多個操做就能夠連接起來,造成一個大的流水線。這讓咱們下一章中的一些優化成爲可能,如延遲和短路。流水線的操做能夠看做對數據源進行數據庫式查詢。
  • 內部迭代——與使用迭代器顯式迭代的集合不一樣,流的迭代操做是在背後進行的。

流與集合

Java現有的集合概念和新的流概念都提供了接口,來配合表明元素型有序值的數據接口。所謂有序,就是說咱們通常是按順序取用值,而不是隨機取用的。那這二者有什麼區別呢?

咱們先來打個直觀的比方吧。好比說存在DVD裏的電影,這就是一個集合(也許是字節,也許是幀,這個無所謂),由於它包含了整個數據結構。如今再來想一想在互聯網上經過視頻流看一樣的電影。如今這是一個流(字節流或幀流)。流媒體視頻播放器只要提早下載用戶觀看位置的那幾幀就能夠了,這樣不用等到流中大部分值計算出來,你就能夠顯示流的開始部分了(想一想觀看直播足球賽)。特別要注意,視頻播放器可能沒有將整個流做爲集合,保存所須要的內存緩衝區——並且要是非得等到最後一幀出現才能開始看,那等待的時間就太長了。出於實現的考慮,你也可讓視頻播放器把流的一部分緩存在集合裏,但和概念上的差別不是一回事。

粗略地說,集合與流之間的差別就在於何時進行計算。集合是一個內存中的數據結構,它包含數據結構中目前全部的值——集合中的每一個元素都得先算出來才能添加到集合中。(你能夠往集合里加東西或者刪東西,可是無論何時,集合中的每一個元素都是放在內存裏的,元素都得先算出來才能成爲集合的一部分。)

相比之下,流則是在概念上固定的數據結構(你不能添加或刪除元素),其元素則是按需計算的。 這對編程有很大的好處。在第6章中,咱們將展現構建一個質數流(2, 3, 5, 7, 11, …)有多簡單,儘管質數有無窮多個。這個思想就是用戶僅僅從流中提取須要的值,而這些值——在用戶看不見的地方——只會按需生成。這是一種生產者-消費者的關係。從另外一個角度來講,流就像是一個延遲建立的集合:只有在消費者要求的時候纔會計算值(用管理學的話說這就是需求驅動,甚至是實時製造)。

與此相反,集合則是急切建立的(供應商驅動:先把倉庫裝滿,再開始賣,就像那些曇花一現的聖誕新玩意兒同樣)。以質數爲例,要是想建立一個包含全部質數的集合,那這個程序算起來就沒完沒了了,由於總有新的質數要算,而後把它加到集合裏面。固然這個集合是永遠也建立不完的,消費者這輩子都見不着了。

流的操做

操做 類型 返回類型 使用的類型、函數式接口 函數描述符
filter 中間 Stream<T> Predicate<T> T -> boolean
distinct 中間(有狀態-無界) Stream<T> `` ``
skip 中間(有狀態-有界) Stream<T> long ``
limit 中間(有狀態-有界) Stream<T> long ``
map 中間 Stream<R> Function<T, R> T -> R
flatMap 中間 Stream<R> Function<T, Stream<R>> T -> Stream<R>
sorted 中間(有狀態-無界) Stream<T> Comparator<T> (T, T) -> int
anyMatch 終端 boolean Predicate<T> T -> boolean
noneMatch 終端 boolean Predicate<T> T -> boolean
allMatch 終端 boolean Predicate<T> T -> boolean
findAny 終端 Optional<T> `` ``
findFirst 終端 Optional<T> `` ``
forEach 終端 void Consumer<T> T -> void
collect 終端 R Collector<T, A, R> ``
reduce`` 終端(有狀態-有界) Optional<T> BinaryOperator<T> (T, T)-> T
count 終端 long `` ``

預約義收集器

Collectors類提供的工廠方法(例如groupingBy)建立的收集器。它們主要提供了三大功能:

  • 將流元素歸約和彙總爲一個值
  • 元素分組
  • 元素分區

Collectors類的靜態工廠方法

工廠方法 返回類型 用於
toList List<T> 把流中全部項目收集到一個List

使用示例:

List<Dish> dishes = menuStream.collect(toList());
工廠方法 返回類型 用於
toSet Set<T> 把流中全部項目收集到一個Set,刪除重複項

使用示例:

Set<Dish> dishes = menuStream.collect(toSet());
工廠方法 返回類型 用於
toCollection Collection<T> 把流中全部項目收集到給定的供應源建立的集合

使用示例:

Collection<Dish> dishes = menuStream.collect(toCollection(),ArrayList::new);
工廠方法 返回類型 用於
counting Long 計算流中元素的個數

使用示例:

long howManyDishes = menuStream.collect(counting());
工廠方法 返回類型 用於
summingInt Integer 對流中項目的一個整數屬性求和

使用示例:

int totalCalories = menuStream.collect(summingInt(Dish::getCalories));
工廠方法 返回類型 用於
averagingInt Double 計算流中項目Integer屬性的平均值

使用示例:

double avgCalories = menuStream.collect(averagingInt(Dish::getCalories));
工廠方法 返回類型 用於
summarizingInt IntSummaryStatistics 收集關於流中項目Integer屬性的統計值,例如最大、最小、總和與平均值

使用示例:

IntSummaryStatistics menuStatistics =
    menuStream.collect(summarizingInt(Dish::getCalories));
工廠方法 返回類型 用於
joining` String 鏈接對流中每一個項目調用toString方法所生成的字符串

使用示例:

String shortMenu =
    menuStream.map(Dish::getName).collect(joining(", "));
工廠方法 返回類型 用於
maxBy Optional<T> 一個包裹了流中按照給定比較器選出的最大元素的Optional,或若是流爲空則爲Optional.empty()

使用示例:

Optional<Dish> fattest =
    menuStream.collect(maxBy(comparingInt(Dish::getCalories)));
工廠方法 返回類型 用於
minBy Optional<T> 一個包裹了流中按照給定比較器選出的最小元素的Optional,或若是流爲空則爲Optional.empty()

使用示例:

Optional<Dish> lightest =
    menuStream.collect(minBy(comparingInt(Dish::getCalories)));
工廠方法 返回類型 用於
reducing 歸約操做產生的類型 從一個做爲累加器的初始值開始,利用BinaryOperator與流中的元素逐個結合,從而將流歸約爲單個值

使用示例:

int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum));
工廠方法 返回類型 用於
collectingAndThen 轉換函數返回的類型 包裹另外一個收集器,對其結果應用轉換函數

使用示例:

int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size));
工廠方法 返回類型 用於
groupingBy Map<K, List<T>> 根據項目的一個屬性的值對流中的項目做問組,並將屬性值做爲結果Map的鍵

使用示例:

Map<Dish.Type,List<Dish>> dishesByType =
    menuStream.collect(groupingBy(Dish::getType));
工廠方法 返回類型 用於
partitioningBy Map<Boolean,List<T>> 根據對流中每一個項目應用謂詞的結果來對項目進行分區

使用示例:

Map<Boolean,List<Dish>> vegetarianDishes =
    menuStream.collect(partitioningBy(Dish::isVegetarian));

並行流

在Java 7以前,並行處理數據集合很是麻煩。第一,你得明確地把包含數據的數據結構分紅若干子部分。第二,你要給每一個子部分分配一個獨立的線程。第三,你須要在恰當的時候對它們進行同步來避免不但願出現的競爭條件,等待全部線程完成,最後把這些部分結果合併起來。Java 7引入了一個叫做分支/合併的框架,讓這些操做更穩定、更不易出錯。

咱們簡要地提到了Stream接口可讓你很是方便地處理它的元素:能夠經過對收集源調用parallelStream方法來把集合轉換爲並行流。並行流就是一個把內容分紅多個數據塊,並用不一樣的線程分別處理每一個數據塊的流。這樣一來,你就能夠自動把給定操做的工做負荷分配給多核處理器的全部內核,讓它們都忙起來。

高效使用並行流

通常而言,想給出任何關於何時該用並行流的定量建議都是不可能也毫無心義的,由於任何相似於「僅當至少有一千個(或一百萬個或隨便什麼數字)元素的時候才用並行流)」的建議對於某臺特定機器上的某個特定操做多是對的,但在略有差別的另外一種狀況下可能就是大錯特錯。儘管如此,咱們至少能夠提出一些定性意見,幫你決定某個特定狀況下是否有必要使用並行流。

  • 若是有疑問,測量。把順序流轉成並行流垂手可得,但卻不必定是好事。咱們在本節中已經指出,並行流並不老是比順序流快。此外,並行流有時候會和你的直覺不一致,因此在考慮選擇順序流仍是並行流時,第一個也是最重要的建議就是用適當的基準來檢查其性能。
  • 留意裝箱。自動裝箱和拆箱操做會大大下降性能。Java 8中有原始類型流(IntStream、LongStream、DoubleStream)來避免這種操做,但凡是有可能都應該用這些流。
  • 有些操做自己在並行流上的性能就比順序流差。特別是limit和findFirst等依賴於元素順序的操做,它們在並行流上執行的代價很是大。例如,findAny會比findFirst性能好,由於它不必定要按順序來執行。你老是能夠調用unordered方法來把有序流變成無序流。那麼,若是你須要流中的n 個元素而不是專門要前n 個的話,對無序並行流調用limit可能會比單個有序流(好比數據源是一個List)更高效。
  • 還要考慮流的操做流水線的總計算成本。設 N 是要處理的元素的總數,Q 是一個元素經過流水線的大體處理成本,則 N*Q 就是這個對成本的一個粗略的定性估計。Q 值較高就意味着使用並行流時性能好的可能性比較大。
  • 對於較小的數據量,選擇並行流幾乎歷來都不是一個好的決定。並行處理少數幾個元素的好處還抵不上並行化形成的額外開銷。
  • 要考慮流背後的數據結構是否易於分解。例如,ArrayList的拆分效率比LinkedList高得多,由於前者用不着遍歷就能夠平均拆分,然後者則必須遍歷。另外,用range工廠方法建立的原始類型流也能夠快速分解。
  • 流自身的特色,以及流水線中的中間操做修改流的方式,均可能會改變分解過程的性能。例如,一個SIZED流能夠分紅大小相等的兩部分,這樣每一個部分均可以比較高效地並行處理,但篩選操做可能丟棄的元素個數卻沒法預測,致使流自己的大小未知。
  • 還要考慮終端操做中合併步驟的代價是大是小(例如Collector中的combiner方法)。若是這一步代價很大,那麼組合每一個子流產生的部分結果所付出的代價就可能會超出經過並行流獲得的性能提高。

流的數據源和可分解性

可分解性
ArrayList 極佳
LinkedList
IntStream.range 極佳
Stream.iterate
HashSet
TreeSet

Optional

Java 8的庫提供了Optional<T>類,這個類容許你在代碼中指定哪個變量的值既多是類型T的值,也多是由靜態方法Optional.empty表示的缺失值。不管是對於理解程序邏輯,抑或是對於編寫產品文檔而言,這都是一個重大的好消息,你如今能夠經過一種數據類型表示顯式缺失的值——使用空指針的問題在於你沒法確切瞭解出現空指針的緣由,它是預期的狀況,仍是說因爲以前的某一次計算出錯致使的一個偶然性的空值,有了Optional以後你就不須要再使用以前容易出錯的空指針來表示缺失的值了。

Optional類的方法

方法 描述
empty 返回一個空的Optional實例
filter 若是值存在而且知足提供的謂詞,就返回包含該值的Optional對象;不然返回一個空的Optional對象
flatMap 若是值存在,就對該值執行提供的mapping函數調用,返回一個Optional類型的值,不然就返回一個空的Optional對象
get 若是該值存在,將該值用Optional封裝返回,不然拋出一個NoSuchElementException異常
ifPresent 若是值存在,就執行使用該值的方法調用,不然什麼也不作
isPresent 若是值存在就返回true,不然返回false
map 若是值存在,就對該值執行提供的mapping函數調用
of 將指定值用Optional封裝以後返回,若是該值爲null,則拋出一個NullPointerException異常
ofNullable 將指定值用Optional封裝以後返回,若是該值爲null,則返回一個空的Optional對象
orElse 若是有值則將其返回,不然返回一個默認值
orElseGet 若是有值則將其返回,不然返回一個由指定的Supplier接口生成的值
orElseThrow 若是有值則將其返回,不然拋出一個由指定的Supplier接口生成的異常

小結

  • null引用在歷史上被引入到程序設計語言中,目的是爲了表示變量值的缺失。
  • Java 8中引入了一個新的類java.util.Optional<T>,對存在或缺失的變量值進行建模。
  • 你可使用靜態工廠方法Optional.empty、Optional.of以及Optional.ofNullable建立Optional對象。
  • Optional類支持多種方法,好比map、flatMap、filter,它們在概念上與Stream類中對應的方法十分類似。
  • 使用Optional會迫使你更積極地解引用Optional對象,以應對變量值缺失的問題,最終,你能更有效地防止代碼中出現不期而至的空指針異常。
  • 使用Optional能幫助你設計更好的API,用戶只須要閱讀方法簽名,就能瞭解該方法是否接受一個Optional類型的值。

CompletableFuture

Java從Java 5版本就提供了Future接口。Future對於充分利用多核處理能力是很是有益的,由於它容許一個任務在一個新的核上生成一個新的子線程,新生成的任務能夠和原來的任務同時運行。原來的任務須要結果時,它能夠經過get方法等待Future運行結束(生成其計算的結果值)。

Future接口的侷限性

咱們知道Future接口提供了方法來檢測異步計算是否已經結束(使用isDone方法),等待異步操做結束,以及獲取計算的結果。可是這些特性還不足以讓你編寫簡潔的併發代碼。好比,咱們很難表述Future結果之間的依賴性;從文字描述上這很簡單,「當長時間計算任務完成時,請將該計算的結果通知到另外一個長時間運行的計算任務,這兩個計算任務都完成後,將計算的結果與另外一個查詢操做結果合併」。可是,使用Future中提供的方法完成這樣的操做又是另一回事。這也是咱們須要更具描述能力的特性的緣由,好比下面這些。

  • 將兩個異步計算合併爲一個——這兩個異步計算之間相互獨立,同時第二個又依賴於第一個的結果。
  • 等待Future集合中的全部任務都完成。
  • 僅等待Future集合中最快結束的任務完成(有可能由於它們試圖經過不一樣的方式計算同一個值),並返回它的結果。
  • 經過編程方式完成一個Future任務的執行(即以手工設定異步操做結果的方式)。
  • 應對Future的完成事件(即當Future的完成事件發生時會收到通知,並能使用Future計算的結果進行下一步的操做,不僅是簡單地阻塞等待操做的結果)。

CompletableFuture 詳解

一個很是有用,不過不那麼精確的格言這麼說:「Completable-Future對於Future的意義就像Stream之於Collection。」讓咱們比較一下這兩者。

  • 經過Stream你能夠對一系列的操做進行流水線,經過map、filter或者其餘相似的方法提供行爲參數化,它可有效避免使用迭代器時老是出現模板代碼。
  • 相似地,CompletableFuture提供了像thenCompose、thenCombine、allOf這樣的操做,對Future涉及的通用設計模式提供了函數式編程的細粒度控制,有助於避免使用命令式編程的模板代碼。

新的日期和時間API

Java的API提供了不少有用的組件,能幫助你構建複雜的應用。不過,Java API也不老是完美的。咱們相信大多數有經驗的程序員都會贊同Java 8以前的庫對日期和時間的支持就很是不理想。然而,你也不用太擔憂:Java 8中引入全新的日期和時間API就是要解決這一問題。

  • LocalDate
  • LocalTime
  • LocalDateTime
  • Instant
  • Duration
  • Period

使用LocalDate和LocalTime還有LocalDateTime

開始使用新的日期和時間API時,你最早碰到的多是LocalDate類。該類的實例是一個不可變對象,它只提供了簡單的日期,並不含當天的時間信息。另外,它也不附帶任何與時區相關的信息。

你能夠經過靜態工廠方法of建立一個LocalDate實例。LocalDate實例提供了多種方法來讀取經常使用的值,好比年份、月份、星期幾等,以下所示。

LocalDate date = LocalDate.of(2014, 3, 18); ←─2014-03-18 int year = date.getYear(); ←─2014 Month month = date.getMonth(); ←─MARCH int day = date.getDayOfMonth(); ←─18 DayOfWeek dow = date.getDayOfWeek(); ←─TUESDAY int len = date.lengthOfMonth(); ←─31 (days in March) boolean leap = date.isLeapYear(); ←─false (not a leap year) //你還可使用工廠方法從系統時鐘中獲取當前的日期: LocalDate today = LocalDate.now();

LocalTime和LocalDateTime都提供了相似的方法。

機器的日期和時間格式

做爲人,咱們習慣於以星期幾、幾號、幾點、幾分這樣的方式理解日期和時間。毫無疑問,這種方式對於計算機而言並不容易理解。從計算機的角度來看,建模時間最天然的格式是表示一個持續時間段上某個點的單一大整型數。這也是新的java.time.Instant類對時間建模的方式,基本上它是以Unix元年時間(傳統的設定爲UTC時區1970年1月1日午夜時分)開始所經歷的秒數進行計算。

你能夠經過向靜態工廠方法ofEpochSecond傳遞一個表明秒數的值建立一個該類的實例。靜態工廠方法ofEpochSecond還有一個加強的重載版本,它接收第二個以納秒爲單位的參數值,對傳入做爲秒數的參數進行調整。重載的版本會調整納秒參數,確保保存的納秒分片在0到999 999 999之間。這意味着下面這些對ofEpochSecond工廠方法的調用會返回幾乎一樣的Instant對象:

Instant.ofEpochSecond(3); Instant.ofEpochSecond(3, 0); Instant.ofEpochSecond(2, 1_000_000_000); ←─2 秒以後再加上100萬納秒(1秒) Instant.ofEpochSecond(4, -1_000_000_000); ←─4秒以前的100萬納秒(1秒)

正如你已經在LocalDate及其餘爲便於閱讀而設計的日期-時間類中所看到的那樣,Instant類也支持靜態工廠方法now,它可以幫你獲取當前時刻的時間戳。咱們想要特別強調一點,Instant的設計初衷是爲了便於機器使用。它包含的是由秒及納秒所構成的數字。因此,它沒法處理那些咱們很是容易理解的時間單位。好比下面這段語句:

int day = Instant.now().get(ChronoField.DAY_OF_MONTH);
它會拋出下面這樣的異常:

java.time.temporal.UnsupportedTemporalTypeException: Unsupported field:

DayOfMonth

可是你能夠經過Duration和Period類使用Instant,接下來咱們會對這部份內容進行介紹。

定義Duration或Period

目前爲止,你看到的全部類都實現了Temporal接口,Temporal接口定義瞭如何讀取和操縱爲時間建模的對象的值。以前的介紹中,咱們已經瞭解了建立Temporal實例的幾種方法。很天然地你會想到,咱們須要建立兩個Temporal對象之間的duration。Duration類的靜態工廠方法between就是爲這個目的而設計的。你能夠建立兩個LocalTimes對象、兩個LocalDateTimes對象,或者兩個Instant對象之間的duration,以下所示:

Duration d1 = Duration.between(time1, time2);
Duration d1 = Duration.between(dateTime1, dateTime2);
Duration d2 = Duration.between(instant1, instant2);

因爲LocalDateTime和Instant是爲不一樣的目的而設計的,一個是爲了便於人閱讀使用,另外一個是爲了便於機器處理,因此你不能將兩者混用。若是你試圖在這兩類對象之間建立duration,會觸發一個DateTimeException異常。此外,因爲Duration類主要用於以秒和納秒衡量時間的長短,你不能僅向between方法傳遞一個LocalDate對象作參數。

若是你須要以年、月或者日的方式對多個時間單位建模,可使用Period類。使用該類的工廠方法between,你可使用獲得兩個LocalDate之間的時長,以下所示:

Period tenDays = Period.between(LocalDate.of(2014, 3, 8), LocalDate.of(2014, 3, 18));

最後,Duration和Period類都提供了不少很是方便的工廠類,直接建立對應的實例;換句話說,就像下面這段代碼那樣,再也不是隻能以兩個temporal對象的差值的方式來定義它們的對象。

建立Duration和Period對象

Duration threeMinutes = Duration.ofMinutes(3); Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES); Period tenDays = Period.ofDays(10); Period threeWeeks = Period.ofWeeks(3); Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);

Duration類和Period類共享了不少類似的方法:

方法名 是不是靜態方法 方法描述
between 建立兩個時間點之間的interval
from 由一個臨時時間點建立interval
of 由它的組成部分建立interval的實例
parse 由字符串建立interval的實例
addTo 建立該interval的副本,並將其疊加到某個指定的temporal對象
get 讀取該interval的狀態
isNegative 檢查該interval是否爲負值,不包含零
isZero 檢查該interval的時長是否爲零
minus 經過減去必定的時間建立該interval的副本
multipliedBy 將interval的值乘以某個標量建立該interval的副本
negated 以忽略某個時長的方式建立該interval的副本
plus 以增長某個指定的時長的方式建立該interval的副本
subtractFrom 從指定的temporal對象中減去該interval
相關文章
相關標籤/搜索