1、图灵社区的电子书没有采用专有客户端,您可以在任意设备上,用自己喜欢的浏览器和PDF阅读器进行阅读。但您购买的电子书仅供您个人使用,未经授权,不得进行传播。我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。?内 容 提 要本书全面介绍了 Java 8 这个里程碑版本的新特性,包括 Lambdas、流和函数式编程。有了函数式的编程特性,可以让代码更简洁,同时也能自动化地利用多核硬件。全书分四个部分:基础知识、函数式数据处理、高效 Java 8 编程和超越 Java 8,清晰明了地向读者展现了
2、一幅 Java 与时俱进的现代化画卷。本书适合广大 Java 开发人员阅读。定价:79.00元读者服务热线:(010)51095186转600印装质量热线:(010)81055316反盗版热线:(010)81055315广告经营许可证:京东工商广字第 8052 号著英 Raoul-Gabriel Urma 意 Mario Fusco 英 Alan Mycroft译陆明刚劳佳责任编辑岳新欣执行编辑郭亚维责任印制彭志环人民邮电出版社出版发行北京市丰台区成寿寺路11号邮编100164电子邮件网址http:/北京印刷开本:80010001/16印张:23 字数:552千字 2016年 4 月第 1 版
3、印数:4 501 5 500册 2016年 11 月北京第 3 次印刷著作权合同登记号 图字:01-2016-1573号(2016.11重印)前 言 1 版 权 声 明 Original English language edition,entitled Java 8 in Action:Lambdas,streams and functional-style programming by Raoul-Gabriel Urma,Mario Fusco,Alan Mycroft,published by Manning Publications.178 South Hill Drive,West
4、ampton,NJ 08060 USA.Copyright 2015 by Manning Publications.Simplified Chinese-language edition copyright 2016 by Posts&Telecom Press.All rights reserved.本书中文简体字版由Manning Publications授权人民邮电出版社独家出版。未经出版者书面许可,不得以任何方式复制或抄袭本书内容。版权所有,侵权必究。谨以此书献给我们的父母。2 前 言 序 言 1998年,八岁的我拿起了我此生第一本计算机书,那本书讲的是JavaScript和HTML
5、我当时怎么也想不到,打开那本书会让我见识编程语言和它们能够创造的神奇世界,并会彻底改变我的生活。我被它深深地吸引了。如今,编程语言的某个新特性还会时不时地让我感到兴奋,因为它让我花更少的时间就能够写出更清晰、更简洁的代码。我希望本书探讨的Java 8中那些来自函数式编程的新思想,同样能够给你启迪。那么,你可能会问,这本书是怎么来的呢?2011年,甲骨文公司的Java语言架构师Brian Goetz分享了一些在Java中添加Lambda表达式的提议,以期获得业界的参与。这让我重新燃起了兴趣,于是我开始传播这些想法,在各种开发人员会议上组织Java 8讨论班,并为剑桥大学的学生开设讲座。到了20
6、13年4月,消息不胫而走,Manning出版社的编辑给我发了封邮件,问我是否有兴趣写一本书关于Java 8中Lambda的书。当时我只是个“不起眼”的二年级博士生,似乎写书并不是一个好主意,因为它会耽误我提交论文。另一方面,所谓“只争朝夕”,我想写一本小书不会有太多工作量,对吧?(后来我才意识到自己大错特错!)于是我咨询我的博士生导师Alan Mycroft教授,结果他十分支持我写书(甚至愿意为这种与博士学位无关的工作提供帮助,我永远感谢他)。几天后,我们见到了Java 8的布道者Mario Fusco,他有着非常丰富的专业经验,并且因在重大开发者会议上所做的函数式编程演讲而享有盛名。我们很快
7、就认识到,如果将大家的能量和背景融合起来,就不仅仅可以写出一本关于Java 8的Lambda的小书,而是可以写出(我们希望)一本五年或十年后,在Java领域仍然有人愿意阅读的书。我们有了一个非常难得的机会来深入讨论许多话题,它们不但有益于Java程序员,还打开了通往一个新世界的大门:函数式编程。15个月后,到2014年7月,在经历无数个漫漫长夜的辛苦工作、无数次的编辑和永生难忘的体验后,我们的工作成果终于送到了你的手上。希望你会喜欢它!Raoul-Gabriel Urma 于剑桥大学 致 谢 1 1 2 3 4 5 8 10 11 9 6 7 致 谢 如果没有许多杰出人士的支持,这本书是不可能
8、完成的。自愿提供宝贵审稿建议的朋友:Richard Walker、Jan Saganowski、Brian Goetz、Stuart Marks、Cem Redif、Paul Sandoz、Stephen Colebourne、igo Mediavilla、Allahbaksh Asadullah、Tomasz Nurkiewicz和Michael Mller。曼宁早期访问项目(Manning Early Access Program,MEAP)中在作者在线论坛上发表评论的读者。在编撰过程中提供有益反馈的审阅者:Antonio Magnaghi、Brent Stains、Franziska M
9、eyer、Furkan Kamachi、Jason Lee、Jrn Dinkla、Lochana Menikarachchi、Mayur Patil、Nikolaos Kaintantzis、Simone Bordet、Steve Rogers、Will Hayworth和William Wheeler。Manning的开发编辑Susan Conant耐心回答了我们所有的问题和疑虑,并为每一章的初稿提供了详尽的反馈,并尽其所能支持我们。Ivan Todorovi和Jean-Franois Morin在本书付印前进行了全面的技术审阅,Al Scherer则在编撰过程中提供了技术帮助。Raoul-
10、Gabriel Urma 首先,我要感谢我的父母在生活中给予我无尽的爱和支持。我写一本书的小小梦想如今成真了!其次,我要向信任并且支持我的博士生导师和合著者Alan Mycroft表达无尽的感激。我也要感谢合著者Mario Fusco陪我走过这段有趣的旅程。最后,我要感谢在生活中为我提供指导、有用建议,给予我鼓励的朋友们:Sophia Drossopoulou、Aidan Roche、Warris Bokhari、Alex Buckley、Martijn Verburg、Tomas Petricek和Tian Zhao。你们真是太棒啦!Mario Fusco 我要特别感谢我的妻子Marilen
11、a,她无尽的耐心让我可以专注于写作本书;还有我们的女儿Sofia,因为她能够创造无尽的混乱,让我可以从本书的写作中暂时抽身。你在阅读本书时将发现,Sofia还用只有两岁小女孩才会的方式,告诉我们内部迭代和外部迭代之间的差异。我还要感谢Raoul-Gabriel Urma和Alan Mycroft,他们与我一起分享了写作本书的(巨大)喜悦和(小小)痛苦。2 致 谢 Alan Mycroft 我要感谢我的太太Hilary和其他家庭成员在本书写作期间对我的忍受,我常常说“再稍微弄弄就好了”,结果一弄就是好几个小时。我还要感谢多年来的同事和学生,他们让我知道了怎么去教授知识。最后,感谢Mario和Ra
12、oul这两位非常高效的合著者,特别是Raoul在苛求“周五再交出一部分稿件”时,还能让人愉快地接受。关于本书 1 1 2 3 4 5 8 10 11 9 6 7 关于本书 简单地说,Java 8中的新增功能是自Java 1.0发布18年以来,Java发生的最大变化。没有去掉任何东西,因此你现有的Java代码都能工作,但新功能提供了强大的新语汇和新设计模式,能帮助你编写更清楚、更简洁的代码。就像遇到所有新功能时那样,你一开始可能会想:“为什么又要去改我的语言呢?”但稍加练习之后,你就会发觉自己只用预期的一半时间,就用新功能写出了更短、更清晰的代码,这时你会意识到自己永远无法返回到“旧Java”了
13、本书会帮助你跨过“原理听起来不错,但还是有点儿新,不太适应”的门槛,从而熟练地进行编程。“也许吧,”你可能会想,“可是Lambda、函数式编程,这些不是那些留着胡子、穿着凉鞋的学究们在象牙塔里面琢磨的东西吗?”或许是的,但Java 8中加入的新想法的分量刚刚好,它们带来的好处也可以被普通的Java程序员所理解。本书会从普通程序员的角度来叙述,偶尔谈谈“这是怎么来的”。“Lambda,听起来跟天书一样!”是的,也许是这样,但它是一个很好的想法,让你可以编写简明的Java程序。许多人都熟悉事件处理器和回调函数,即注册一个对象,它包含会在事件发生时使用的一个方法。Lambda使人更容易在Java中
14、广泛应用这种思想。简单来说,Lambda和它的朋友“方法引用”让你在做其他事情的过程中,可以简明地将代码或方法作为参数传递进去执行。在本书中,你会看到这种思想出现得比预想的还要频繁:从加入作比较的代码来简单地参数化一个排序方法,到利用新的Stream API在一组数据上表达复杂的查询指令。“流(stream)是什么?”这是Java 8的一个新功能。它们的特点和集合(collection)差不多,但有几个明显的优点,让我们可以使用新的编程风格。首先,如果你使用过SQL等数据库查询语言,就会发现用几行代码写出的查询语句要是换成Java要写好长。Java 8的流支持这种简明的数据库查询式编程但用的是
15、Java语法,而无需了解数据库!其次,流被设计成无需同时将所有的数据调入内存(甚至根本无需计算),这样就可以处理无法装入计算机内存的流数据了。但Java 8可以对流做一些集合所不能的优化操作,例如,它可以将对同一个流的若干操作组合起来,从而只遍历一次数据,而不是花很大代价去多次遍历它。更妙的是,Java可以自动将流操作并行化(集合可不行)。“还有函数式编程,这又是什么?”就像面向对象编程一样,它是另一种编程风格,其核心是把函数作为值,前面在讨论Lambda的时候提到过。2 关于本书 Java 8的好处在于,它把函数式编程中一些最好的想法融入到了大家熟悉的Java语法中。有了这个优秀的设计选择,
16、你可以把函数式编程看作Java 8中一个额外的设计模式和语汇,让你可以用更少的时间,编写更清楚、更简洁的代码。想想你的编程兵器库中的利器又多了一样。当然,除了这些在概念上对Java有很大扩充的功能,我们也会解释很多其他有用的Java 8功能和更新,如默认方法、新的Optional类、CompletableFuture,以及新的日期和时间API。别急,这只是一个概览,现在该让你自己去看看本书了。本书结构 本书分为四个部分:“基础知识”“函数式数据处理”“高效Java 8编程”和“超越Java 8”。我们强烈建议你按顺序阅读,因为很多概念都需要前面的章节作为基础。大多数章节都有几个小测验,帮助你学
17、习和掌握这些内容。第一部分包括3章,旨在帮助你初步使用Java 8。学完这一部分,你将会对Lambda表达式有充分的了解,并可以编写简洁而灵活的代码,能够轻松适应不断变化的需求。在第1章中,我们总结了Java的主要变化(Lambda表达式、方法引用、流和默认方法),并为学习后面的内容做好准备。在第2章中,你将了解行为参数化,这是Java 8非常依赖的一种软件开发模式,也是引入Lambda表达式的主要原因。第3章全面地解释了Lambda表达式和方法引用,每一步都有代码示例和测验。第二部分仔细讨论了新的Stream API。学完这一部分,你将充分理解流是什么,以及如何在Java应用程序中使用它们来
18、简洁而高效地处理数据集。第4章介绍了流的概念,并解释它们与集合有何异同。第5章详细讨论了表达复杂数据处理查询可以使用的流操作。我们会谈到很多模式,如筛选、切片、查找、匹配、映射和归约。第6章讲到了收集器Stream API的一个功能,可以让你表达更为复杂的数据处理查询。在第7章中,你将了解流如何得以自动并行执行,并利用多核架构的优势。此外,你还会学到为正确而高效地使用并行流,要避免的若干陷阱。第三部分探讨了能让你高效使用Java 8并在代码中运用现代语汇的若干内容。第8章探讨了如何利用Java 8的新功能和一些秘诀来改善你现有的代码。此外,该章还探讨了一些重要的软件开发技术,如设计模式、重构、
19、测试和调试。在第9章中,你将了解到默认方法是什么,如何利用它们来以兼容的方式演变API,一些实际的应用模式,以及有效使用默认方法的规则。第10章谈到了新的java.util.Optional类,它能让你设计出更好的API,并减少空指针异常。第11章探讨了CompletableFuture,它可以让你用声明性方式表达复杂的异步计算,从而让Stream API的设计并行化。关于本书 3 1 2 3 4 5 8 10 11 9 6 7 第12章探讨了新的日期和时间API,这相对于以前涉及日期和时间时容易出错的API是一大改进。在本书最后一部分,我们会返回来谈谈怎么用Java编写高效的函数式程序,还会
20、将Java 8的功能和Scala作一比较。第13章是一个完整的函数式编程教程,介绍了一些术语,并解释了如何在Java 8中编写函数式风格的程序。第14章涵盖了更高级的函数式编程技巧,包括高阶函数、科里化、持久化数据结构、延迟列表和模式匹配。你可以把这一章看作一种融合,既有可以用在代码库中的实际技术,也有让你成为更渊博的程序员的学术知识。第15章对比了Java 8的功能与Scala的功能。Scala和Java一样,是一种实施在JVM上的语言,近年来迅速发展,在编程语言生态系统中已经威胁到了Java的一些方面。在第16章我们会回顾这段学习Java 8并慢慢走向函数式编程的历程。此外,我们还会猜测,
21、在Java 8之后,未来可能还有哪些增强和新功能出现。最后,本书有四个附录,涵盖了与Java 8相关的其他一些话题。附录A总结了本书未讨论的一些Java 8的小特性。附录B概述了Java库的其他主要扩展,可能对你有用。附录C是第二部分的延续,谈到了流的高级用法。附录D探讨了Java编译器在幕后是如何实现Lambda表达式的。代码惯例和下载 所有代码清单和正文中的源代码都采用等宽字体(如fixed-widthfontlikethis),以与普通文字区分开来。许多代码清单中都有注释,突出了重要的概念。书中所有示例代码和执行说明均可见于https:/ 购买本书即可免费访问Manning Public
22、ations运营的一个私有在线论坛,你可以在那里发表关于本书的评论、询问技术问题,并获得作者和其他用户的帮助。如欲访问作者在线论坛并订阅,请用浏览器访问https:/ 前 言 关于封面图 本书封面上的图为“1700年中国清朝满族战士的服饰”。图片中的人物衣饰华丽,身佩利剑,背背弓和箭筒。如果你仔细看他的腰带,会发现一个形的带扣(这是我们的设计师加上去的,暗示本书的主题)。该图选自托马斯杰弗里斯的各国古代和现代服饰集(A Collection of the Dresses of Different Nations,Ancient and Modern,伦敦,1757年至1772年间出版),该书标
23、题页中说这些图是手工上色的铜版雕刻品,并且是用阿拉伯树胶填充的。托马斯杰弗里斯(Thomas Jefferys,17191771)被称为“乔治三世的地理学家”。他是一名英国制图员,是当时主要的地图供应商。他为政府和其他官方机构雕刻和印制地图,制作了很多商业地图和地理地图集,尤以北美地区为多。地图制作商的工作让他对勘察和绘图过的地方的服饰产生了兴趣,这些都在这个四卷本中得到了出色的展现。向往遥远的土地、渴望旅行,在18世纪还是相对新鲜的现象,而类似于这本集子的书籍则十分流行,这些集子向旅游者和坐着扶手椅梦想去旅游的人介绍了其他国家的人。杰弗里斯书中异彩纷呈的图画生动地描绘了几百年前世界各国的独特
24、与个性。如今,着装规则已经改变,各个国家和地区一度非常丰富的多样性也已消失,来自不同大陆的人仅靠衣着已经很难区分开了。不过,要是乐观点儿看,我们这是用文化和视觉上的多样性,换得了更多姿多彩的个人生活或是更为多样化、更为有趣的知识和技术生活。计算机书籍一度也是如此繁荣,Manning出版社在此用杰弗里斯画中复活的三个世纪前风格各异的国家服饰,来象征计算机行业中的发明与创造的异彩纷呈。目 录 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 18 17 目 录 第一部分 基础知识 第 1 章 为什么要关心 Java 8.2 1.1 Java 怎么还在变.4 1.1.1
25、Java 在编程语言生态系统中的位置.4 1.1.2 流处理.6 1.1.3 用行为参数化把代码传递给 方法.7 1.1.4 并行与共享的可变数据.7 1.1.5 Java 需要演变.8 1.2 Java 中的函数.8 1.2.1 方法和 Lambda 作为一等公民.9 1.2.2 传递代码:一个例子.11 1.2.3 从传递方法到 Lambda.12 1.3 流.13 1.4 默认方法.17 1.5 来自函数式编程的其他好思想.18 1.6 小结.19 第 2 章 通过行为参数化传递代码.20 2.1 应对不断变化的需求.21 2.1.1 初试牛刀:筛选绿苹果.21 2.1.2 再展身手:把
26、颜色作为参数.21 2.1.3 第三次尝试:对你能想到的每个属性做筛选.22 2.2 行为参数化.23 2.3 对付啰嗦.27 2.3.1 匿名类.28 2.3.2 第五次尝试:使用匿名类.28 2.3.3 第六次尝试:使用 Lambda 表达式.30 2.3.4 第七次尝试:将List类型 抽象化.31 2.4 真实的例子.31 2.4.1 用Comparator来排序.31 2.4.2 用Runnable执行代码块.32 2.4.3 GUI 事件处理.32 2.5 小结.33 第 3 章 Lambda 表达式.34 3.1 Lambda 管中窥豹.35 3.2 在哪里以及如何使用 Lamb
27、da.37 3.2.1 函数式接口.37 3.2.2 函数描述符.39 3.3 把 Lambda 付诸实践:环绕执行模式.41 3.3.1 第 1 步:记得行为参数化.41 3.3.2 第 2 步:使用函数式接口来 传递行为.42 3.3.3 第 3 步:执行一个行为.42 3.3.4 第 4 步:传递 Lambda.42 3.4 使用函数式接口.43 3.4.1 Predicate.44 3.4.2 Consumer.44 3.4.3 Function.45 3.5 类型检查、类型推断以及限制.49 3.5.1 类型检查.49 3.5.2 同样的 Lambda,不同的 函数式接口.50 3.
28、5.3 类型推断.51 2 目 录 3.5.4 使用局部变量.52 3.6 方法引用.53 3.6.1 管中窥豹.53 3.6.2 构造函数引用.55 3.7 Lambda 和方法引用实战.57 3.7.1 第 1 步:传递代码.58 3.7.2 第 2 步:使用匿名类.58 3.7.3 第 3 步:使用 Lambda 表达式.58 3.7.4 第 4 步:使用方法引用.59 3.8 复合 Lambda 表达式的有用方法.59 3.8.1 比较器复合.60 3.8.2 谓词复合.60 3.8.3 函数复合.61 3.9 数学中的类似思想.62 3.9.1 积分.62 3.9.2 与 Java
29、8 的 Lambda 联系起来.63 3.10 小结.64 第二部分 函数式数据处理 第 4 章 引入流.68 4.1 流是什么.68 4.2 流简介.72 4.3 流与集合.74 4.3.1 只能遍历一次.75 4.3.2 外部迭代与内部迭代.76 4.4 流操作.78 4.4.1 中间操作.78 4.4.2 终端操作.79 4.4.3 使用流.80 4.5 小结.81 第 5 章 使用流.82 5.1 筛选和切片.83 5.1.1 用谓词筛选.83 5.1.2 筛选各异的元素.83 5.1.3 截短流.84 5.1.4 跳过元素.85 5.2 映射.86 5.2.1 对流中每一个元素应用函
30、数.86 5.2.2 流的扁平化.87 5.3 查找和匹配.90 5.3.1 检查谓词是否至少匹配一个 元素.90 5.3.2 检查谓词是否匹配所有元素.90 5.3.3 查找元素.91 5.3.4 查找第一个元素.92 5.4 归约.92 5.4.1 元素求和.93 5.4.2 最大值和最小值.94 5.5 付诸实践.97 5.5.1 领域:交易员和交易.98 5.5.2 解答.99 5.6 数值流.101 5.6.1 原始类型流特化.101 5.6.2 数值范围.102 5.6.3 数值流应用:勾股数.103 5.7 构建流.105 5.7.1 由值创建流.106 5.7.2 由数组创建流
31、106 5.7.3 由文件生成流.106 5.7.4 由函数生成流:创建无限流.107 5.8 小结.110 第 6 章 用流收集数据.111 6.1 收集器简介.112 6.1.1 收集器用作高级归约.112 6.1.2 预定义收集器.113 6.2 归约和汇总.114 6.2.1 查找流中的最大值和最小值.114 6.2.2 汇总.115 6.2.3 连接字符串.116 6.2.4 广义的归约汇总.117 6.3 分组.120 6.3.1 多级分组.121 6.3.2 按子组收集数据.122 6.4 分区.126 6.4.1 分区的优势.126 目 录 3 2 3 4 5 6 7 8 9
32、 10 11 12 13 14 15 16 18 17 6.4.2 将数字按质数和非质数分区.128 6.5 收集器接口.129 6.5.1 理解Collector接口声明的 方法.130 6.5.2 全部融合到一起.134 6.6 开发你自己的收集器以获得 更好的性能.135 6.6.1 仅用质数做除数.136 6.6.2 比较收集器的性能.139 6.7 小结.140 第 7 章 并行数据处理与性能.141 7.1 并行流.141 7.1.1 将顺序流转换为并行流.142 7.1.2 测量流性能.144 7.1.3 正确使用并行流.147 7.1.4 高效使用并行流.148 7.2 分支/
33、合并框架.149 7.2.1 使用RecursiveTask.149 7.2.2 使用分支/合并框架的 最佳做法.153 7.2.3 工作窃取.154 7.3 Spliterator.155 7.3.1 拆分过程.155 7.3.2 实现你自己的Spliterator.157 7.4 小结.162 第三部分 高效Java 8编程 第 8 章 重构、测试和调试.164 8.1 为改善可读性和灵活性重构代码.164 8.1.1 改善代码的可读性.165 8.1.2 从匿名类到 Lambda 表达式的 转换.165 8.1.3 从 Lambda 表达式到方法引用 的转换.166 8.1.4 从命令式
34、的数据处理切换到Stream.167 8.1.5 增加代码的灵活性.168 8.2 使用 Lambda 重构面向对象的 设计模式.170 8.2.1 策略模式.171 8.2.2 模板方法.172 8.2.3 观察者模式.173 8.2.4 责任链模式.175 8.2.5 工厂模式.177 8.3 测试 Lambda 表达式.178 8.3.1 测试可见 Lambda 函数的 行为.179 8.3.2 测试使用 Lambda 的方法的 行为.179 8.3.3 将复杂的 Lambda 表达式分到不同的方法.180 8.3.4 高阶函数的测试.180 8.4 调试.181 8.4.1 查看栈跟踪
35、181 8.4.2 使用日志调试.183 8.5 小结.184 第 9 章 默认方法.185 9.1 不断演进的 API.187 9.1.1 初始版本的 API.188 9.1.2 第二版 API.188 9.2 概述默认方法.190 9.3 默认方法的使用模式.192 9.3.1 可选方法.192 9.3.2 行为的多继承.192 9.4 解决冲突的规则.196 9.4.1 解决问题的三条规则.196 9.4.2 选择提供了最具体实现的默认 方法的接口.197 9.4.3 冲突及如何显式地消除歧义.198 9.4.4 菱形继承问题.200 9.5 小结.201 第 10 章 用Option
36、al取代null.202 10.1 如何为缺失的值建模.203 10.1.1 采用防御式检查减少Null-PointerException.203 4 目 录 10.1.2 null带来的种种问题.204 10.1.3 其他语言中null的 替代品.205 10.2 Optional类入门.206 10.3 应用Optional的几种模式.207 10.3.1 创建Optional对象.208 10.3.2 使用map从Optional 对象中提取和转换值.208 10.3.3 使用flatMap链接 Optional对象.209 10.3.4 默认行为及解引用 Optional对象.213
37、10.3.5 两个Optional对象的 组合.213 10.3.6 使用filter剔除特定 的值.214 10.4 使用Optional的实战示例.216 10.4.1 用Optional封装可能为 null的值.216 10.4.2 异常与Optional的对比.217 10.4.3 把所有内容整合起来.218 10.5 小结.219 第 11 章 CompletableFuture:组合式 异步编程.220 11.1 Future接口.222 11.1.1 Future接口的局限性.223 11.1.2 使用CompletableFuture 构建异步应用.223 11.2 实现异步
38、API.224 11.2.1 将同步方法转换为异步 方法.225 11.2.2 错误处理.227 11.3 让你的代码免受阻塞之苦.228 11.3.1 使用并行流对请求进行并行 操作.229 11.3.2 使用CompletableFuture 发起异步请求.230 11.3.3 寻找更好的方案.232 11.3.4 使用定制的执行器.233 11.4 对多个异步任务进行流水线操作.234 11.4.1 实现折扣服务.235 11.4.2 使用Discount服务.236 11.4.3 构造同步和异步操作.237 11.4.4 将两个Completable-Future对象整合起来,无论它们
39、是否存在依赖.239 11.4.5 对Future和Completable-Future的回顾.241 11.5 响应CompletableFuture的 completion事件.242 11.5.1 对最佳价格查询器应用的 优化.243 11.5.2 付诸实践.244 11.6 小结.245 第 12 章 新的日期和时间 API.246 12.1 LocalDate、LocalTime、Instant、Duration以及Period.247 12.1.1 使用LocalDate和 LocalTime.247 12.1.2 合并日期和时间.248 12.1.3 机器的日期和时间格式.249
40、 12.1.4 定义Duration或 Period.249 12.2 操纵、解析和格式化日期.251 12.2.1 使用TemporalAdjuster.253 12.2.2 打印输出及解析日期时间 对象.255 12.3 处理不同的时区和历法.256 12.3.1 利用和 UTC/格林尼治时间 的固定偏差计算时区.257 12.3.2 使用别的日历系统.258 12.4 小结.259 第四部分 超越Java 8 第 13 章 函数式的思考.262 13.1 实现和维护系统.262 13.1.1 共享的可变数据.263 目 录 5 2 3 4 5 6 7 8 9 10 11 12 13 14
41、 15 16 18 17 13.1.2 声明式编程.264 13.1.3 为什么要采用函数式编程.265 13.2 什么是函数式编程.265 13.2.1 函数式 Java 编程.266 13.2.2 引用透明性.268 13.2.3 面向对象的编程和函数式 编程的对比.268 13.2.4 函数式编程实战.269 13.3 递归和迭代.271 13.4 小结.274 第 14 章 函数式编程的技巧.275 14.1 无处不在的函数.275 14.1.1 高阶函数.275 14.1.2 科里化.277 14.2 持久化数据结构.278 14.2.1 破坏式更新和函数式更新的 比较.279 14
42、2.2 另一个使用Tree的例子.281 14.2.3 采用函数式的方法.282 14.3 Stream 的延迟计算.283 14.3.1 自定义的 Stream.283 14.3.2 创建你自己的延迟列表.286 14.4 模式匹配.290 14.4.1 访问者设计模式.291 14.4.2 用模式匹配力挽狂澜.292 14.5 杂项.295 14.5.1 缓存或记忆表.295 14.5.2“返回同样的对象”意味着 什么.296 14.5.3 结合器.296 14.6 小结.297 第 15 章 面向对象和函数式编程的混合:Java 8 和 Scala 的比较.299 15.1 Scala
43、 简介.300 15.1.1 你好,啤酒.300 15.1.2 基础数据结构:List、Set、Map、Tuple、Stream 以及Option.302 15.2 函数.306 15.2.1 Scala 中的一等函数.307 15.2.2 匿名函数和闭包.307 15.2.3 科里化.309 15.3 类和 trait.310 15.3.1 更加简洁的 Scala 类.310 15.3.2 Scala 的 trait 与 Java 8 的 接口对比.311 15.4 小结.312 第 16 章 结论以及 Java 的未来.313 16.1 回顾 Java 8 的语言特性.313 16.1.1
44、 行为参数化(Lambda 以及 方法引用).314 16.1.2 流.314 16.1.3 CompletableFuture.315 16.1.4 Optional.315 16.1.5 默认方法.316 16.2 Java 的未来.316 16.2.1 集合.316 16.2.2 类型系统的改进.317 16.2.3 模式匹配.318 16.2.4 更加丰富的泛型形式.319 16.2.5 对不变性的更深层支持.321 16.2.6 值类型.322 16.3 写在最后的话.325 附录 A 其他语言特性的更新.326 附录 B 类库的更新.330 附录 C 如何以并发方式在同一个流上 执
45、行多种操作.338 附录 D Lambda 表达式和 JVM 字 节码.346 第一部分基础知识Part 1本书第一部分将介绍Java 8的基础知识。学完第一部分,你将会对Lambda表达式有充分的了解,并可以编写简洁而灵活的代码,能够轻松地适应不断变化的需求。第1章将总结Java的主要变化(Lambda表达式、方法引用、流和默认方法),并为学习本书做好准备。在第2章中,你将了解行为参数化,这是Java 8非常依赖的一种软件开发模式,也是引入Lambda表达式的主要原因。第3章全面地解释了Lambda表达式和方法引用的概念,每一步都有代码示例和测验。2 第 1章 为什么要关心 Java 8 为
46、什么要关心Java 8 本章内容 Java怎么又变了 日新月异的计算应用背景:多核和处理大型数据集(大数据)改进的压力:函数式比命令式更适应新的体系架构 Java 8的核心新特性:Lambda(匿名函数)、流、默认方法 自1998年JDK 1.0(Java 1.0)发布以来,Java已经受到了学生、项目经理和程序员等一大批活跃用户的欢迎。这一语言极富活力,不断被用在大大小小的项目里。从Java 1.1(1997年)一直到Java 7(2011年),Java通过增加新功能,不断得到良好的升级。Java 8则是在2014年3月发布的。那么,问题来了:为什么你应该关心Java 8?我们的理由是,Ja
47、va 8所做的改变,在许多方面比Java历史上任何一次改变都深远。而且好消息是,这些改变会让你编起程来更容易,用不着再写类似下面这种啰嗦的程序了(对inventory中的苹果按照重量进行排序):Collections.sort(inventory,new Comparator()public int compare(Apple a1,Apple a2)return a1.getWeight().compareTo(a2.getWeight(););在Java 8里面,你可以编写更为简洁的代码,这些代码读起来更接近问题的描述:inventory.sort(comparing(Apple:getW
48、eight);它念起来就是“给库存排序,比较苹果的重量”。现在你不用太关注这段代码,本书后面的章节将会介绍它是做什么用的,以及你如何写出类似的代码。Java 8对硬件也有影响:平常我们用的CPU都是多核的你的笔记本电脑或台式机上的处理器可能有四个CPU内核,甚至更多。但是,绝大多数现有的Java程序都只使用其中一个内核,其他三个都闲着,或只是用一小部分的处理能力来运行操作系统或杀毒程序。在Java 8之前,专家们可能会告诉你,必须利用线程才能使用多个内核。问题是,线程用起来很难,也容易出现错误。从Java的演变路径来看,它一直致力于让并发编程更容易、出错更少。第 1 章本书中第一段Java 8
49、的代码!1.1 Java怎么还在变 3 1 2 3 4 5 8 10 9 6 7 12 11 13 16 14 15 Java 1.0里有线程和锁,甚至有一个内存模型这是当时的最佳做法,但事实证明,不具备专门知识的项目团队很难可靠地使用这些基本模型。Java 5添加了工业级的构建模块,如线程池和并发集合。Java 7添加了分支/合并(fork/join)框架,使得并行变得更实用,但仍然很困难。而Java 8对并行有了一个更简单的新思路,不过你仍要遵循一些规则,本书中会谈到。我们用两个例子(它们有更简洁的代码,且更简单地使用了多核处理器)就可以管中窥豹,看到一座拔地而起相互勾连一致的Java 8
50、大厦。首先让你快速了解一下这些想法(希望能引起你的兴趣,也希望我们总结得足够简洁):Stream API 向方法传递代码的技巧 接口中的默认方法 Java 8提供了一个新的API(称为“流”,Stream),它支持许多处理数据的并行操作,其思路和在数据库查询语言中的思路类似用更高级的方式表达想要的东西,而由“实现”(在这里是Streams库)来选择最佳低级执行机制。这样就可以避免用synchronized编写代码,这一代码不仅容易出错,而且在多核CPU上执行所需的成本也比你想象的要高。从有点修正主义的角度来看,在Java 8中加入Streams可以看作把另外两项扩充加入Java 8的直接原因:






