java8新特性
Java8 新特性总结
1 | 一、lamada、函数式编程 |
一、lamada、函数式编程
函数式编程:
即行为参数化;
lamada:
简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列列表、函数主体、返回类型,可能还有⼀一个可以︎出的异常列列表。
特点:︎︎︎︎匿名、函数、简洁、传递
可以在函数式接口上使⽤用Lambda表达式。
函数式接⼝:只定义⼀个抽象⽅方法的接⼝
1 | demo |
二、Stream介绍:
在调⽤collect之前,没有任何结果产⽣,实际上根本就没有从menu⾥选择元素。 流的处理过程
是一种内部迭代,** 你可以这么理解:链中的⽅方法调⽤用都在排队等待,直到调⽤用collect。Java 8的Stream以
其延迟︎︎性而著称,它们被︎刻意设计成这样,即︎︎︎︎︎︎延迟操作,有其独特的原因: Stream就像是⼀一个︎盒,它接收请
求⽣成结果。当你向一个 Stream发起一系列列的操作请求时,这 些请求只是被⼀一保存起来。只有当你
向Stream发起⼀个︎︎︎作时,才会实际地进⾏计算。这种设计具有显著的优点,特别是你需要对Stream进⾏多个操作时(你有可能先要进⾏filter操 作,紧接着做⼀个map,最后进⾏⼀次终端操作reduce;这种 ⽅式下Stream只需要遍历⼀次, 不需要为每个操作遍历⼀次所有的元素.
粗略地说,集合与流之间的差异就在于什么时进⾏计算。
图4-2显示了流操作的顺序:filter、map、limit、collect 每个操作简介如下。 举个例⼦:
1 | List<String> dishenameList = menu.stream().filter(d -> d.getCalories() > |
流只能遍历⼀次。遍历完之后,我们就说这个流已经被消费了,——Streams库的内部迭代可以⾃动选择 ⼀种适 合你硬件的数据表示和并⾏实现。流利⽤了内部迭代:替你把迭代做了.
Stream流⽔水线解决⽅方案
Stream上的所有操作分为两类:中间操作和结束操作,中间操作只是一种标记,只有结束操作才会
触发实际计算。中间操作⼜可以分为⽆状态的( Stateless )和有状态的( Stateful ),⽆无状态中间操作是指元素
的处理理不不受前面元素的影响,而有状态的中间操作必须等到所有元素处理理之后才知道最终结果,⽐如排
序是有状态操作,在读取所有元素之前并不能确定排序结果;结束操作又可以分为短路操作和非短路操作,短路操作是指不用处理全部元素就可以返回结果,⽐比如找到第一个满⾜足条件的元素。之所以要进行如此精细的划分,是因为底层对每⼀种情况的处理方式不同。
很多Stream操作会需要⼀个回调函数(Lambda表达式),因此一个完整的操作是<*数据来源,操作、回调函数 > 构成的三元组。 Stream 中使⽤用 Stage 的概念来描述⼀个完整的操作,并⽤某种实例例化后的PipelineHelper*来代表Stage,将具有先后顺序的各个Stage连到⼀起,就构成了整个流⽔水线。跟
Stream相关类和接口的继承关系图示。
Stream流⽔水线组织结构示意图如下:
图中通过 Collection.stream() ⽅法得到Head也就是stage0,紧接着调⽤⼀系列的中间操作,不断产 ⽣新的Stream。这些Stream对象以双向链表的形式组织在⼀起,构成整个流⽔线,由于每个Stage都 记录了前⼀个Stage和本次的操作以及回调函数,依靠这种结构就能建⽴起对数据源的所有操作。这就 是Stream记录操作的⽅式。当前Stage本身才知道该如何执⾏⾃⼰包含的动作。这就需要有某种协议来 协调相邻Stage之间的调⽤关系。这种协议由Sink接⼝完成.
实际上Stream API内部实现的的本质,就是如何重载Sink的这四个接⼝口⽅方法
1 | 遍历元素时调⽤用,接受⼀一个待处理理元素,并对元素进⾏行行处理理。Stage |
图中通过Collection.stream()⽅方法得到 Head 也就是stage0,紧接着调⽤用⼀一系列列的中间操作,不不断产
⽣生新的Stream。 这些Stream对象以双向链表的形式组织在⼀一起,构成整个流⽔水线,由于每个Stage都
记录了了前⼀一个Stage和本次的操作以及回调函数,依靠这种结构就能建⽴立起对数据源的所有操作 。这就
是Stream记录操作的⽅方式。当前Stage本身才知道该如何执⾏行行⾃自⼰己包含的动作。这就需要有某种协议来
协调相邻Stage之间的调⽤用关系。这种协议由 Sink 接⼝口完成
实际上Stream API内部实现的的本质,就是如何重载Sink的这四个接⼝口⽅方法
Sink完美封装了了Stream每⼀步操作,并给出了[处理->转发]的模式来叠加操作。
举例:
总⽽⾔之,流的使⽤⼀般包括三件事:
⼀个数据(如集合)来执⾏⼀个查询;
⼀个中间作,形成⼀条流的流⽔线;
⼀个操作,执⾏流⽔线,并能⽣成结果。
以上,流的流⽔线背后的理念类似于构建器模式。在构建器模式中有⼀个调⽤链⽤来设⼀套配 (对流来 说这就是⼀个中间操作链),接着是调⽤built⽅法(对流来说就是终端操作)。
如你所⻅,Stream API实现如此巧妙,即使我们使⽤外部迭代⼿动编写等价代码,也未必更加⾼效。
1 | demo |
三、默认⽅方法:
它提供的能⼒力力能帮助类库的设计 者们定义新的操作,增强接⼝口的能⼒力力,它们︎蔽了了将来的变化对 ⽤用户的
影响.
比如:List类1.8新增的foreach⽅法
1 | * for each element action The action to be performed |
四、异步处理理
CompletableFuture
CompletableFuture提供了了像thenCompose、thenCombine、allOf这样的 操作,对Future︎及的通⽤用设
计模式提供了了函数式编程的细︎度控制,有助于避免使⽤用 命令式编程的模︎代码。
五、时间处理理类
LocalDate ︎ LocalTime ︎ Instant ︎ Duration 以︎及 Period 极⼤简化⽅便了日期处理⽅式,包括:
- Java 8之前⽼版的java.util.Date类以及其他⽤于建模⽇期时间的类有很多不⼀致及 设计上的缺,包括易变性以及的值、默认值和命名。
- 新版的⽇期和时间API中,⽇期时间对象是不可变的。
- 新的API提供了两种不同的时间表示⽅式,有效地区分了运⾏时⼈和机器的不同需求。
- 可以⽤绝对或者相对的⽅式操⽇期和时间,操作的结果总是返回⼀个新的实例,⽼的⽇期时间对象不会发⽣变化。
- TemporalAdjuster让你能够⽤更精细的⽅式操⽇期,不再限于⼀次只能改变它的 ⼀个值,并且你还可按照需求定义⾃⼰的⽇期转换器。 你现在可以按照特定的格式需求,定义⾃⼰的格式器,打印输出或者解⽇期时间对象。 这些格式器可以通过模创建,也可以⾃⼰编程创建,并且它们都是线程安全的。
- 可以⽤相对于某个地区/位的⽅式,或者以与UTC/格尼时间的绝对差的⽅式表 示时区,并将其应⽤到⽇期时间对象上,对其进⾏本地化。
- 可以使⽤不同于ISO-8601标准系统的其他⽇历系统
六、why java8?
函数式编程:
- 实现和维护系统,无需担心锁引起的各种问题,充分发︎系统的并发能力;
- 共享的可变数据,︎︎纯粹且⽆作用.在完全无锁的情况下,使⽤用多核的并发机制;
- 下⾯面是这⼀章中你应该掌握的关键概念。
- 从⻓远看,︎少共享的可变数据结构能帮助你︎低维护和调︎程序的代价。
- 函数式编程⽀持⽆副作⽤的方法和声明式编程。
- 函数式方法可以由它的输入参数及输出结果进行︎断。
- 如果一个函数使用相同的参数值调用,总是返回相同的结果,那么它是引⽤用透明的。采用递︎可以取得迭代式的结构,⽐比如while循环。
- 相对于Java语言中传统的递︎,“︎︎递”可能是⼀种更好的⽅式,它开启了一︎扇门,让我们有机会最终使用编译器进行优化。
七、函数编程技巧
- ⾼高︎阶函数:
1 | Function<String, String> transformationPipeline |
函数科⾥化(⻅上⽂demo)
Stream 延迟计算 :
1 | // 延迟计算: |
模式匹配? Java8⽀持的不太好,感觉作⽤不太⼤,代替switch case
实现缓存记忆表: ⽐如Map::computeIfAbsent()⽅法
1 | // cache类似DP算法中的记录薄,基于function,computeIfAbsent每次会将表达式计算结果暂存,提升递归性能,非常的优雅, // 同样的实现,在java8前会很麻烦 |
- 结合器 ⽐如:CompletableFuture::thenCombine(),该⽅法接受两个CompletableFuture⽅法 和⼀个BiFunction⽅法,返回 另⼀个CompletableFuture⽅法。
1 | demo: |
其他:
1
2
3
4
5
6
7// 4、骚操作:实现一个失败后重试最多5次的代码
Stream.generate(() -ss> Math.random() > 0.8 ? "ok" : null)
.limit(5)
.filter(Objects::nonNull)
.findFirst()
.ifPresent(System.out::println);
}Idea ⾃带Refactor Function 骚操作:
八、超越java 8—jdk版本重大特性总结
Java 8 : lambda 函数式计算、 stream 、 JVM 废弃永久代 , 引入 metaSpace 区
Java9 : Jigsaw 模块系统 ( 打包模块化 )
增强了 Stream , Optional , Process API
新增 HTTP2 Client
Java 10: 新增局部类型推断 var
Java 11 : Lambda 表达式中使用 var , 引入 ZGC( 实验功能 )
Java 12 : switch 表达式扩展 , 增强 g1 ( 自动返回未使用堆给操作系统 )
java 13 : 增强 ZGC( 释放未使用内存 ) 、引入文本块功能
Java 14: 移除CMS收集器
Java 15(2020-9-15 发布 ): ZGC 、 Shenandoah 垃圾回器 从实验功能变为产品功能
九、结论总结
1 、行为参数化(Lambda以及方法引用)
这些值具有类似Function<T, R>、Predicate或者BiFunction<T, U, R>这样的类型,值的接收方可以通过
apply、test或其他类似的⽅法执行这些方法。Lambda表达式自身是 一个相当酷︎的概念,不不过Java 8对
它们的使用方式——将它们与全新的Stream API相结合,最终把它们推向了了新一代Java的核⼼。
2 、流
它采用︎︎算法将这些操作组成一个流水线,通过单次流遍历, 一次性完成所有的操作。
它的parallel⽅法能帮助将一个Stream标记为适合进⾏并⾏处理。
3 、 CompletableFuture
CompletableFuture提供了了像thenCompose、thenCombine、allOf这样的 操作,对Future︎及的通⽤用设
计模式提供了了函数式编程的细︎度控制,有助于避免使用命令式编程的模︎代码。
4 、optional
如果在程序中始终如一地使用Optional,你的应⽤应该永远不会发⽣生NullPointerException异常。
5 、默认⽅法
它提供的能力能帮助类库的设计者们定义新的操作,增强接口的能力,它们︎蔽了将来的变化对用户的
影响.
十、附录
Lambda表︎式和JVM︎字节码
编译时,匿匿名类和Lambda表达式使⽤了不不同的字节码指令。(javap -c -v ClassName)
Lambda 创建额外的类 现在被invokedynamic指令替代了了。这种方式使⽤用 invokedynamic ,可以将实现
Lambda表达式的这部分代码的字节码⽣成 推︎到运行时。这种情况下,编译器可以生成 ⼀一个方法,该⽅
法含有该Lambda表达式同样的签名.
参考:
流介绍: https://www.edjdhbb.com/2019/02/23/java-8-stream%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86/
《Java8 实战》https://github.com/zxiaofan/JavaBooks/blob/master/Java%208%E5%AE%9E%E6%88%98.pdf