Java8实战笔记下篇
- 11.6 小结
- 12.4 小结
- 13.4 小结
- 14.6 小结
- 15.4 小结
- 16.1.5 默认方法
11.6 小结
这一章中,你学到的内容如下。
执行比较耗时的操作时,尤其是那些依赖一个或多个远程服务的操作,使用异步任务可以改善程序的性能,加快程序的响应速度。
你应该尽可能地为客户提供异步API。使用CompletableFuture类提供的特性,你能够轻松地实现这一目标。
CompletableFuture类还提供了异常管理的机制,让你有机会抛出/管理异步任务执行中发生的异常。
将同步API的调用封装到一个CompletableFuture中,你能够以异步的方式使用其结果。
如果异步任务之间相互独立,或者它们之间某一些的结果是另一些的输入,你可以将这些异步任务构造或者合并成一个。
你可以为CompletableFuture注册一个回调函数,在Future执行完毕或者它们计算的结果可用时,针对性地执行一些程序。
你可以决定在什么时候结束程序的运行,是等待由CompletableFuture对象构成的列表中所有的对象都执行完毕,还是只要其中任何一个首先完成就中止程序的运行。
12.4 小结
这一章中,你应该掌握下面这些内容。
Java 8之前老版的java.util.Date类以及其他用于建模日期时间的类有很多不一致及设计上的缺陷,包括易变性以及糟糕的偏移值、默认值和命名。
新版的日期和时间API中,日期时间对象是不可变的。
新的API提供了两种不同的时间表示方式,有效地区分了运行时人和机器的不同需求。
你可以用绝对或者相对的方式操纵日期和时间,操作的结果总是返回一个新的实例,老的日期时间对象不会发生变化。
TemporalAdjuster让你能够用更精细的方式操纵日期,不再局限于一次只能改变它的一个值,并且你还可按照需求定义自己的日期转换器。
你现在可以按照特定的格式需求,定义自己的格式器,打印输出或者解析日期时间对象。这些格式器可以通过模板创建,也可以自己编程创建,并且它们都是线程安全的。
你可以用相对于某个地区/位置的方式,或者以与UTC/格林尼治时间的绝对偏差的方式表示时区,并将其应用到日期时间对象上,对其进行本地化。
你现在可以使用不同于ISO-8601标准系统的其他日历系统了。
采用这种“要做什么”风格的编程通常被称为声明式编程。你制定规则,给出了希望实现的目标,让系统来决定如何实现这个目标。它带来的好处非常明显,用这种方式编写的代码更加接近问题陈述了。
这意味着调用者需要检查方法返回的是否为一个空的Optional对象。这件事听起来代价不小,依据我们之前对函数式编程和纯粹的函数式编程的比较,从实际操作的角度出发,你可以选择在本地局部地使用异常,避免通过接口将结果暴露给其他方法,这种方式既取得了函数式的优点,又不会过度膨胀代码。
13.4 小结
下面是这一章中你应该掌握的关键概念。
从长远看,减少共享的可变数据结构能帮助你降低维护和调试程序的代价。
函数式编程支持无副作用的方法和声明式编程。
函数式方法可以由它的输入参数及输出结果进行判断。
如果一个函数使用相同的参数值调用,总是返回相同的结果,那么它是引用透明的。采用递归可以取得迭代式的结构,比如while循环。
相对于Java语言中传统的递归,“尾递”可能是一种更好的方式,它开启了一扇门,让我们有机会最终使用编译器进行优化。
14.6 小结
下面是本章中你应该掌握的重要概念。
一等函数是可以作为参数传递,可以作为结果返回,同时还能存储在数据结构中的函数。
高阶函数接受至少一个或者多个函数作为输入参数,或者返回另一个函数的函数。Java中典型的高阶函数包括comparing、andThen和compose。
科里化是一种帮助你模块化函数和重用代码的技术。
持久化数据结构在其被修改之前会对自身前一个版本的内容进行备份。因此,使用该技术能避免不必要的防御式复制。
Java语言中的Stream不是自定义的。
延迟列表是Java语言中让Stream更具表现力的一个特性。延迟列表让你可以通过辅助方(supplier)即时地创建列表中的元素,辅助方法能帮忙创建更多的数据结构。
模式匹配是一种函数式的特性,它能帮助你解包数据类型。它可以看成Java语言中switch语句的一种泛化。
遵守“引用透明性”原则的函数,其计算结构可以进行缓存。
结合器是一种函数式的思想,它指的是将两个或多个函数或者数据结构进行合并。
15.4 小结
下面是这一章中介绍的关键概念和你应该掌握的要点。
Java 8和Scala都是整合了面向对象编程和函数式编程特性的编程语言,它们都运行于JVM之上,在很多时候可以相互操作。
Scala支持对集合的抽象,支持处理的对象包括List、Set、Map、Stream、Option,这些和Java 8非常类似。不过,除此之外Scala还支持元组。
Scala为函数提供了更加丰富的特性,这方面比Java 8做得好,Scala支持:函数类型、可以不受限制地访问本地变量的闭包,以及内置的科里化表单。
Scala中的类可以提供隐式的构造器、getter方法以及setter方法。
Scala还支持trait,它是一种同时包含了字段和默认方法的接口。
Java 8通过借鉴函数式编程,提供了一种新的方式——通过向方法传递代码片段来解决这一问题。这种新的方法非常方便地提供了两种变体。
传递一个Lambda表达式,即一段精简的代码片段,比如apple -> apple.getWeight() > 150
传递一个方法引用,该方法引用指向了一个现有的方法,比如这样的代码:Apple::isHeavy
集合到底有什么问题,以至于我们需要另起炉灶替换掉它们,或通过一个类似却不同的概念Stream对其进行增强。我们把二者之间的差异概括如下:如果你有一个数据量庞大的集合,你需要对这个集合应用三个操作,比如对这个集合中的对象进行映射,对其中的两个字段进行求和,这之后依据某种条件过滤出满足条件的和,最后对结果进行排序,即为得到结果你需要分三次遍历集合。Stream API则与之相反,它采用延迟算法将这些操作组成一个流水线,通过单次流遍历,一次性完成所有的操作。对于大型的数据集,这种操作方式要高效得多。不过,还有一些需要我们考虑的因素,比如内存缓存,数据集越大,越需要尽可能地减少遍历的次数。
16.1.5 默认方法
Java 8中增加了不少新特性,但是它们一般都不对个体程序的行为带来影响。不过,有一件事情是例外,那就是新增的默认方法。接口中新引入的默认方法对类库的设计者而言简直是如鱼得水。Java 8之前,接口主要用于定义方法签名,现在它们还能为接口的使用者提供方法的默认实现,如果接口的设计者认为接口中声明的某个方法并不需要每一个接口的用户显式地提供实现,他就可以考虑在接口的方法声明中为其定义默认方法。对类库的设计者而言,这是个伟大的新工具,原因很简单,它提供的能力能帮助类库的设计者们定义新的操作,增强接口的能力,类库的用户们(即那些实现该接口的程序员们)不需要花费额外的精力重新实现该方法。因此,默认方法与库的用户也有关系,它们屏蔽了将来的变化对用户的影响。第9章针对这一问题进行了更加深入的探讨。自此,我们已经完成了对Java 8中新概念的总结。现在我们会转向更为棘手的主题,那就是Java 8之后的版本中可能会有哪些新的改进以及新的特性出现。
Java中有两种类型的值:一类是对象类型,它们包含着可变的字段(除非使用了final关键字进行修饰),对这种类型值的特征,可以使用==进行比较;还有一类是值类型,这种类型的变量是不能改变的,也不带任何的引用特征(reference identity),简单类型就属于这种更宽泛意义上的值类型。这样,我们就能创建用户自定义值的类型了(这种类型的变量推荐小写字符开头,突出它们与int和boolean这类简单类型的相似性)。对于值类型,默认情况下,硬件对int进行比较时会以一个字节接着一个字节逐次的方式进行,==会以同样的方式一个元素接着一个元素地对两个变量进行比较。