摘要:但有一個限制它們不能修改定義的方法的局部變量的內容。如前所述,這種限制存在的原因在于局部變量保存在棧上,并且隱式表示它們僅限于其所在線程。
2014年,Oracle發布了Java8新版本。對于Java來說,這顯然是一個具有里程碑意義的版本。尤其是那函數式編程的功能,避開了Java那煩瑣的語法所帶來的麻煩。
這可以算是一篇Java8的學習筆記。將Java8一些常見的一些特性作了一個概要的筆記。
行為參數化(Lambda以及方法引用)為了編寫可重用的方法,比如filter,你需要為其指定一個參數,它能夠精確地描述過濾條件。雖然Java專家們使用之前的版本也能達到同樣的目的(將過濾條件封裝成類的一個方法,傳遞該類的一個實例),但這種方案卻很難推廣,因為它通常非常臃腫,既難于編寫,也不易于維護。
Java 8通過借鑒函數式編程,提供了一種新的方式——通過向方法傳遞代碼片段來解決這一問題。這種新的方法非常方便地提供了兩種變體。
傳遞一個Lambda表達式,即一段精簡的代碼片段,比如
apple -> apple.getWeight() > 150
傳遞一個方法引用,該方法引用指向了一個現有的方法,比如這樣的代碼:
Apple::isHeavy
這些值具有類似Function
閉包函數接口
你可能已經聽說過閉包(closure,不要和Clojure編程語言混淆)這個詞,你可能會想Lambda是否滿足閉包的定義。用科學的說法來說,閉包就是一個函數的實例,且它可以無限制地訪問那個函數的非本地變量。例如,閉包可以作為參數傳遞給另一個函數。它也可以訪問和修改其作用域之外的變量。現在,Java 8的Lambda和匿名類可以做類似于閉包的事情:它們可以作為參數傳遞給方法,并且可以訪問其作用域之外的變量。但有一個限制:它們不能修改定義Lambda的方法的局部變量的內容。這些變量必須是隱式最終的。可以認為Lambda是對值封閉,而不是對變量封閉。如前所述,這種限制存在的原因在于局部變量保存在棧上,并且隱式表示它們僅限于其所在線程。如果允許捕獲可改變的局部變量,就會引發造成線程不安全的新的可能性,而這是我們不想看到的(實例變量可以,因為它們保存在堆中,而堆是在線程之間共享的)。
Java 8之前,接口主要用于定義方法簽名,現在它們還能為接口的使用者提供方法的默認實現,如果接口的設計者認為接口中聲明的某個方法并不需要每一個接口的用戶顯式地提供實現,他就可以考慮在接口的方法聲明中為其定義默認方法。
對類庫的設計者而言,這是個偉大的新工具,原因很簡單,它提供的能力能幫助類庫的設計者們定義新的操作,增強接口的能力,類庫的用戶們(即那些實現該接口的程序員們)不需要花費額外的精力重新實現該方法。因此,默認方法與庫的用戶也有關系,它們屏蔽了將來的變化對用戶的影響。
在接口上添加注解:@FunctionalInterface。即可聲明該接口為函數接口。
如果你去看看新的Java API,會發現函數式接口帶有@FunctionalInterface的標注。這個標注用于表示該接口會設計成一個函數式接口。如果你用@FunctionalInterface定義了一個接口,而它卻不是函數式接口的話,編譯器將返回一個提示原因的錯誤。例如,錯誤消息可能是“Multiple non-overriding abstract methods found in interface Foo”,表明存在多個抽象方法。請注意,@FunctionalInterface不是必需的,但對于為此設計的接口而言,使用它是比較好的做法。它就像是@Override標注表示方法被重寫了。
Lambdas及函數式接口的例子:
使用案例 | Lambda例子 | 對應的函數式接口 |
---|---|---|
布爾表達式 | (List |
Predicate
|
創建對象 | () -> new Apple(10) | Supplier |
消費一個對象 | (Apple a) ->System.out.println(a.getWeight()) | Consumer |
從一個對象中選擇/提取 | (String s) -> s.length() | Function |
合并兩個值 | (int a, int b) -> a * b | IntBinaryOperator |
比較兩個對象 | (Apple a1, Apple a2) ->a1.getWeight().compareTo(a2.getWeight()) | Comparator |
要討論流,我們先來談談集合,這是最容易上手的方式了。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 |
Predicate |
T -> boolean |
distinct | 中間(有狀態-無界) | Stream |
`` | `` |
skip | 中間(有狀態-有界) | Stream |
long | `` |
limit | 中間(有狀態-有界) | Stream |
long | `` |
map | 中間 | Stream |
Function |
T -> R |
flatMap | 中間 | Stream |
Function |
T -> Stream |
sorted | 中間(有狀態-無界) | Stream |
Comparator |
(T, T) -> int |
anyMatch | 終端 | boolean | Predicate |
T -> boolean |
noneMatch | 終端 | boolean | Predicate |
T -> boolean |
allMatch | 終端 | boolean | Predicate |
T -> boolean |
findAny | 終端 | Optional |
`` | `` |
findFirst | 終端 | Optional |
`` | `` |
forEach | 終端 | void | Consumer |
T -> void |
collect | 終端 | R | Collector |
`` |
reduce`` | 終端(有狀態-有界) | Optional |
BinaryOperator |
(T, T)-> T |
count | 終端 | long | `` | `` |
即Collectors類提供的工廠方法(例如groupingBy)創建的收集器。它們主要提供了三大功能:
將流元素歸約和匯總為一個值
元素分組
元素分區
Collectors類的靜態工廠方法
工廠方法 | 返回類型 | 用于 |
---|---|---|
toList | List |
把流中所有項目收集到一個List |
使用示例:
Listdishes = menuStream.collect(toList());
工廠方法 | 返回類型 | 用于 |
---|---|---|
toSet | Set |
把流中所有項目收集到一個Set,刪除重復項 |
使用示例:
Setdishes = menuStream.collect(toSet());
工廠方法 | 返回類型 | 用于 |
---|---|---|
toCollection | Collection |
把流中所有項目收集到給定的供應源創建的集合 |
使用示例:
Collectiondishes = 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 |
一個包裹了流中按照給定比較器選出的最大元素的Optional,或如果流為空則為Optional.empty() |
使用示例:
Optionalfattest = menuStream.collect(maxBy(comparingInt(Dish::getCalories)));
工廠方法 | 返回類型 | 用于 |
---|---|---|
minBy | Optional |
一個包裹了流中按照給定比較器選出的最小元素的Optional,或如果流為空則為Optional.empty() |
使用示例:
Optionallightest = 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 |
根據項目的一個屬性的值對流中的項目作問組,并將屬性值作為結果Map的鍵 |
使用示例:
Map> dishesByType = menuStream.collect(groupingBy(Dish::getType));
工廠方法 | 返回類型 | 用于 |
---|---|---|
partitioningBy | Map |
根據對流中每個項目應用謂詞的結果來對項目進行分區 |
使用示例:
Map并行流> 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 | 好 |
Java 8的庫提供了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
你可以使用靜態工廠方法Optional.empty、Optional.of以及Optional.ofNullable創建Optional對象。
Optional類支持多種方法,比如map、flatMap、filter,它們在概念上與Stream類中對應的方法十分相似。
使用Optional會迫使你更積極地解引用Optional對象,以應對變量值缺失的問題,最終,你能更有效地防止代碼中出現不期而至的空指針異常。
使用Optional能幫助你設計更好的API,用戶只需要閱讀方法簽名,就能了解該方法是否接受一個Optional類型的值。
CompletableFutureJava從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涉及的通用設計模式提供了函數式編程的細粒度控制,有助于避免使用命令式編程的模板代碼。
新的日期和時間APIJava的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 |
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/66043.html
摘要:等待其安裝完成后關閉程序,重新啟動,點開菜單可見項,說明插件管理包已安裝成功。在出現的懸浮對話框中輸入然后點選下面的插件,就會自動開始安裝,請耐心等待。【注:以下內容參考https://blog.csdn.net/stilling2006/article/details/54376743】 一、認識Sublime text 1、一款跨平臺代碼編輯器,在Linux、OSX和Windows下均可...
摘要:寫在前面最近在學習,遇到有些頁面請求數據需要用戶登錄權限服務器響應不符預期的問題,但是總不能每個頁面都做單獨處理吧,于是想到提供了攔截器這個好東西,再于是就出現了本文。 1.寫在前面 最近在學習Vue2,遇到有些頁面請求數據需要用戶登錄權限、服務器響應不符預期的問題,但是總不能每個頁面都做單獨處理吧,于是想到axios提供了攔截器這個好東西,再于是就出現了本文。 2.具體需求 用戶鑒...
摘要:前段時間為了抓取網絡文本數據,申請了騰訊云學生機,用的框架弄了一段時間。這個用戶既是不可登錄的操作系統用戶,也是數據庫用戶。設置數據庫用戶密碼為了能夠讓和數據庫相連接,需要設置數據庫用戶密碼。 打讀研之后,更加關注算法的學習,Web開發這一塊便落下了,平時也通過微信公眾號關注了些,常常感慨,技術的更迭真是日新月異。 前段時間為了抓取網絡文本數據,申請了騰訊云學生機,用Python的Sc...
閱讀 955·2021-09-26 09:55
閱讀 3212·2021-09-22 15:36
閱讀 2993·2021-09-04 16:48
閱讀 3150·2021-09-01 11:41
閱讀 2600·2019-08-30 13:49
閱讀 1500·2019-08-29 18:46
閱讀 3554·2019-08-29 17:28
閱讀 3437·2019-08-29 14:11