Java8学习小记

2014年,Oracle发布了Java8新版本。对于Java来讲,这显然是一个具备里程碑意义的版本。尤为是那函数式编程的功能,避开了Java那烦琐的语法所带来的麻烦。java

这能够算是一篇Java8的学习笔记。将Java8一些常见的一些特性做了一个概要的笔记。程序员

行为参数化(Lambda以及方法引用)

为了编写可重用的方法,好比filter,你须要为其指定一个参数,它可以精确地描述过滤条件。虽然Java专家们使用以前的版本也能达到一样的目的(将过滤条件封装成类的一个方法,传递该类的一个实例),但这种方案却很难推广,由于它一般很是臃肿,既难于编写,也不易于维护。数据库

Java 8经过借鉴函数式编程,提供了一种新的方式——经过向方法传递代码片断来解决这一问题。这种新的方法很是方便地提供了两种变体。编程

  • 传递一个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以前,接口主要用于定义方法签名,如今它们还能为接口的使用者提供方法的默认实现,若是接口的设计者认为接口中声明的某个方法并不须要每个接口的用户显式地提供实现,他就能够考虑在接口的方法声明中为其定义默认方法。数组

对类库的设计者而言,这是个伟大的新工具,缘由很简单,它提供的能力能帮助类库的设计者们定义新的操做,加强接口的能力,类库的用户们(即那些实现该接口的程序员们)不须要花费额外的精力从新实现该方法。所以,默认方法与库的用户也有关系,它们屏蔽了未来的变化对用户的影响。缓存

在接口上添加注解:@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
相关文章
相关标签/搜索