Java 8新特性
lambda表达式
也可称为闭包
闭包又称词法闭包
解释一:闭包是引用了自由变量的函数,这个被引用的变量将和这个函数一同存在。
解释二:闭包是函数和相关引用环境组成的实体。
1 | (parameters)->expression |
主要特征
- 可选类型声明:不需要声明参数类型,编译器统一识别参数值
- 可选的参数圆括号:一个参数无需定义圆括号,多个参数需要定义圆括号
- 可选大括号:主题包含了一个语句,就不需要使用大括号
- 可选的返回关键字:主体只有一个表达式返回值则编译器自动返回值,大括号需要指定表达式返回一个数值
1 | //lambda表达式例子 |
注意事项:
- lambda表达式主要用来执行定义行内执行的方法类型接口。
- 免去了使用匿名方法的麻烦,简单强大的函数式编程。
lambda表达式中的局部变量具有final语义(即是final类型,或者必须不被后面代码修改)
lambda表达式中的this
lambda表达式和內部匿名类的区别就内部匿名类中的this指向的是内部匿名类本身,而lambda表达式中的类指的是当前类。
1 | package jdk8.lambda; |
Java中this实现原理
lambda表达式中,会把lambda表达式在本类中生产一个lambda$+数字的方法。关键点:该方法不一定是static方法,是否是static方法,取决于lambda表达式里面是否引用了this。
1 | // lambda实现 |
用javap -s -p 类名可以看出
1 | PS D:\CodeHub\Java8\out\production\Java8\com\jdk8\lambda> javap -s -p '.\ThisDemo.class' |
Lambda实例方法的方法引用
方法引用有多种,静态方法的方法引用很好理解,但是实例对象的方法引用有点难以理解
1 | public class MethodDemo { |
java里面在默认把this作为参数,放到实例方法的第一个参数
Lambda表达式实现惰性求值
惰性求值在Lambda表达式中非常重要,也十分有用。
1 | // 打印日志前需要先判断日志级别 |
为什么要判断日志级别,
因为不判断执行代码,发现虽然日志没有打印,但toString方法还是执行了,属于多余浪费的开销。
每一个日志打印都加判断,看着很别扭,现在有了lambda表达式之后,可以使用lambda的惰性求值,就可以去掉if判断,如下
1 | // 使用lambda表达式的惰性求值,不需要判断日志级别 |
lambda惰性求值底层机制
这个现象很好理解,简单讲解一下。就是没有使用表达式的时候,相当于
1 | String msg = "打印一些日志:" + this |
虽然最后没有打印,但字符串拼接的工作还是执行了。而使用了lambda表达式之后,字符串的拼接放到一个函数里面,fine日志需要打印的时候才去调用这个方法才真正执行!从而实现了惰性求值。
后面我们学习的jdk8的stream流编程里面,没有调用最终操作的时候,中间操作的方法都不会执行,这也是惰性求值。
lambda表达式作用域
访问局部变量
我们可以直接在lambda表达式中访问外部的局部变量:
1 | final int num = 1; |
但是和匿名对象不同的是,这里的变量num可以不用声明为final,该代码同样正确
1 | int num = 1; |
但是这里的num必须不可以被后面的代码修改
访问字段和静态变量
和局部变量相比,我们对lambda表达式中的实例字段和静态变量都有读写访问权限
1 | class lambda4{ |
访问默认接口方法
无法在lambda表达式访问默认方法,下面代码无法编译
1 |
Java8–foreach使用
foreach:新的简洁而有趣的迭代集合的方法;
基础知识
1 | public interface Collection<E> extends Iterable<E> |
Collection接口实现了iterable接口,而iterable接口在java8开始具有一个新的api
1 | void forEach(Consumer<? super T> action) //对Iterable的每个元素执行特定的操作,直到所有元素被处理或者引发异常 |
迭代和打印字符串的for循环版本
1 | for(String name : names){ |
我们可以使用forEach写这个
1 | names.forEach(name->{ |
使用forEach方法
使用forEach迭代集合并且对每个元素执行特定操作,要执行的操作包含在实现Consumer接口的类中 ,并且作为参数传递给forEach。
消费者接口是一个功能接口,接受输入并且不返回任何结果。
Consumer接口定义
1 |
|
任何实现,例如只是打印字符串的消费者
1 | Consumer<String> printConsumer = new Consumer<String>(){ |
可以作为参数传递给forEach:
1 | names.forEach(printConsumer); |
我们可以使用方法引用语法而不是普通的lambda语法
1 | names.forEach(System.out::println) |
forEach在集合里的使用
迭代集合
正如我们已经看到的迭代列表的元素
1 | List<String> names = Arrays.asList("Larry", "Steve", "James"); |
同样对于一组:
1 | Set<String> uniqueNames = new HashSet<>(Arrays.asList("Larry", "Steve", "James")); |
或者让我们说一个队列也是一个集合:
1 | Queue<String> namesQueue = new ArrayDeque<>(Arrays.asList("Larry", "Steve", "James")); |
迭代Map
迭代Map - 使用Map的forEach
Map没有实现Iterable接口,但它提供了自己的forEach 变体,它接受BiConsumer。*
1 | Map<Integer, String> namesMap = new HashMap<>(); |
4.3.迭代一个Map - 通过迭代entrySet
1 | namesMap.entrySet().forEach(entry -> System.out.println(entry.getKey() + " " + entry.getValue())); |
Stream流编程
Stream内部迭代和外部迭代
首先分为中间操作和最终操作,在最终操作没有被调用的情况下,所有的中级操作都不会执行。
简单来说,返回Stream流就是中间操作,可以继续链式调用下去,不返回stream就是最终操作。
最终操作分为短路操作和非短路操作,
中间操作又分为有状态操作和无状态操作。状态就是和其他数据有关系。
Stream运行机制
通过下面代码理解流的运行机制
1 | package stream; |
JDK9的响应式流
叫做reactive Stream,也就是flow,是一个发布订阅模式
1 | import java.util.concurrent.Flow.Processor; |
响应式基石Reactor
Spring webflux基于reactor实现响应式,那么reactor是什么,
reactor = jdk8Stream + jdk9flow响应流
通过代码理解
1 | import java.util.concurrent.TimeUnit; |
Spring5中的webflux
webflux的关键在于自己编写代码返回(Flux|Mono),Spring框架用来负责处理订阅。Spring提供两种开发模式来编写响应式代码,使用MVC注解或者使用 router function模式。
异步servlet
学习异步servlet的关键在于了解同步servlet阻塞了什么,为什么需要异步servlet,异步servlet支持高吞吐量的原理是什么?
servlet容器,没处理一次请求都会占用一个线程,同步servlet里面业务代码处理多久,servlet容器的线程阻塞多久。如果servlet线程用完,就无法再处理请求了。
在异步servlet中,servlet容器的线程不会傻傻的等待业务代码处理完毕,而是直接返回,给业务代码一个回调函数,业务代码处理完了在通知。这样就可以用少量线程处理更高的请求,从而实现高吞吐量。
1 | true,urlPatterns = {"/AsyncServlet"}) (asyncSupported= |
SSE(server-sent event)
1 | "/SSE") ( |
同步异步和水平垂直扩展
打个比喻,假设现在有一堆的任务完成不了,当前的生产力无法完成,需要扩展的话,那么简单来说,水平扩展就是加人,垂直扩展就是加班l。人不够,加人就是水平扩展,那么加人肯定是比较简单的,一个人做不完就2个,2个不够就10个,总有够的一天。加人容易解决,但是成本也高啊!所以你是老板的话,你更加乐意是让员工垂直扩展-加班,人还是一个人,活多了!加班和加人不一样,是有极限的,一个人加的班是由一定限度的。这里的加班不一定是工作时间的加长,更加多的是工作能力的提升,个人的成长。怎么样才能加强工作能力?就是改变工作模式,把自己的处理任务模式优化一下,让相同的资源能处理更加多的任务,如把小任务集中处理,任务管理的统筹学等,这些都能提高效率。
Webflux为什么支持高吞吐量
响应式编程里面,tomcat容器的线程根本不需要阻塞的等待!等任务处理完了,会通知tomcat容器,这就是异步!