介绍
Java8新特性简介
-
速度更快
-
代码更少(lambda表达式)
-
强大的Stream API
-
最大化减少空指针异常(Optional)
Java8 的兼容机制
- 兼容机制
在Java语言中,一个接口中定义的方法必须由实现类提供实现。但是当接口中加入新的API时, 实现类按照约定也要修改实现,而Java8的API对现有接口也添加了很多方法,比如List接口中添加了sort方法。 如果按照之前的做法,那么所有的实现类都要实现sort方法,java的做法是,在Java8种引入新的机制,default关键字,支持在接口中声明方法同时提供实现。
特性解释
- 速度更快
java 8 在底层数据数据结构上的改变,核心之一就是HashMap,将原来的数组-链表存储方式转化为,数组–链表–红黑树。当链表长度超过8,总容量超过64,则将链表自动转化为红黑树。这将很大的提升我们数据的删改查以及当数组扩容后数据存放位置的速率。
扩容后,存放在红黑树中的数据无需通过哈希算法再次计算Hash code 而是根据情况选择保持在原地或将其存放原索引+旧容量的位置。例如原存放位置为5,原容量为16,扩展为32位,则现在存储的位置为21。
相应改变的也有ConcurrentHashMap,它取消了并发级别,而是改用CAS算法,它为底层支持的算法,效率要比锁高尚很多。
另一个改变的为底层内存结构,将堆中的永久区(加载类信息)的方法剥离出来,将其转换成Metaspace(元空间),去除堆的永久区,元空间改为使用物理内存。改变的同时JVM调优的两个参数–>PremGenSize及MaxPremGenSize也替换成MeatSpaceSize及MaxMetaSpaceSize。由于元空间使用物理内存,大大的增加了空间,而垃圾回收机制则是在空间将要满时,进行垃圾回收,这将很大的减小回收方法的几率,提高了垃圾回收机制的效率。OutOfMemoryError发生的概率也同时减小,加快我们编程的效率。
- lambda表达式
基本語法格式:
(parameters) -> expression
左边是参数,右边是功能表达式,你可以像在函数里面写各种功能,把你想要实现的功能都写在右边。
但是java是怎么实现我们写的这些表达式的呢?
下面看一下先看一下四种表达式:
/**
*lambda表达式和函数型接口
* 语法: (parameters) -> expression
* 用法:作为实现的lambda接口的函数参数传递
*/
// 1. 接收一个参数,没有返回值 ---Consumer
Consumer<String> tConsumer = (s) -> System.out.println("Hello, " + s);
// 2. 接受0个参数,返回一个随机数 ---Supplier
Supplier<Integer> integerSupplier = () -> (int) (Math.random() * 100);
// 3. 接收一个参数,返回其2倍的值 ---Function
Function<Integer, Integer> integerFunction = x -> 2 * x;
// 5. 接受一个参数对象,返回一个boolean值 ---Predicate
Predicate<String> tPredicate = (s) -> s.length() > 0;
lambda表达式的实现是依赖于这些函数接口的,每一个表达式必须要有对应类型的函数接口才能实现。上面这些lambda表达式都依赖于对应的函数接口,上面四个表达式就对应下表四个java8内置的函数式接口。
(lambda表达式用法见下文)
函数式接口 其实就是一个普通的接口,但是接口内只允许定义一个抽象方法(可以定义其他的普通方法)。
除了java8内置的函数式接口,我们也可以用@FunctionalInterface注解自定义函数式接口。
函数式接口常用的实现方式,一个是lambda表达式,一个是方法引用。
方法引用就是引用类里面已经实现的方法。
引用格式:
类名::静态方法名
引用名(对象名)::实例方法名
类名::实例方法名
方法引用方式实现函数式接口
/**
* 方法引用:
* 类名::静态方法名
* 引用名(对象名)::实例方法名
* 类名::实例方法名
*/
Consumer c = System.out::println;
Supplier<Optional> empty = Optional::empty;
Predicate<Optional> isPresent = Optional::isPresent;
Function<String, Optional<String>> of = Optional::of;
- Stream API
stream流给List提供了一种非常便捷强大的的处理方式。基本上你可以流结合lambda表达式来完成任何你想要的操作。
stream中提供了很多譬如筛选,切片,规约的功能函数,这些函数都是支持lambda表达式的,也就是说,在使用的时候,可以把lambda表达式作为参数传进去。
下面看stream结合lambda表达式的使用:
/**
* Stream API
* 结合lambda表达式使用
*/
List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");
//forEach需要一个函数来对过滤后的元素依次执行。结束操作
//filter 筛选 中间
stringCollection
.stream()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
// "aaa2", "aaa1"
//Sort 排序
//排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。
stringCollection
.stream()
.sorted()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
// "aaa1", "aaa2"
//排序只创建了一个排列好后的Stream,而不会影响原有的数据源
//Map 映射
//
//中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,
// 下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,
// map返回的Stream类型是根据你map传递进去的函数的返回值决定的。
stringCollection
.stream()
.map(String::toUpperCase)
.sorted((a, b) -> b.compareTo(a))
.forEach(System.out::println);
// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
//Match 匹配
//Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。
// 所有的匹配操作都是最终操作,并返回一个boolean类型的值。
boolean anyStartsWithA =
stringCollection
.stream()
.anyMatch((s) -> s.startsWith("a"));
//Reduce 规约
//这是一个最终操作,允许通过指定的函数来讲stream中的多个元素规约为一个元素,
// 规越后的结果是通过Optional接口表示的:
Optional<String> reduced =
stringCollection
.stream()
.sorted()
.reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
除此之外,java8很多其他的函数提供了lambda表达式式的参数支持,这种兼容也是通过default关键字实现的。
- Optional空指针利器
Optional 一个对象容器,用来处理空指针问题,可以放T类型的对象,也可以直接放空值。
Optional 是用来处理空指针问题的,但是不是说只有用了 Optional 就不会出现空指针问题,下面这两种情况,依然会报空指针异常。
//空指针情况
Optional optional = Optional.of(null);
Optional optional1 = Optional.empty();
optional1.get();
当然,这种情况的空指针一般比较好定位。
一般当我们不能确保调用的方法返回不为空时,我们就可以使用 Optional 来放返回值。
Optional 还提供了一个ifPresent方法,用Consumer接口作为参数,当 Optional存放的对象不为空时,就会执行参数表达式里面的命令。相当于一个回调函数。
。
项目中的应用实例
根据条件查询es语句。
通常看到的很多情况都是通过一层层的for语句加循环条件的判断来获取es中的字段的,这样子一个是影响可读性,另一个会很容易写错,还有就是不美观。下面是用lambda+Stream的实现方式
/**
*1.根据fuwuNo找到订单里面满足要求的服务订单fuwuOrderRefOP
*2.遍历服务单里面的乘客航段fuwuProductSnapRefOP,找到ext_info="productJson"的ExtInfoRef
*3.从Ext_value对象里面获取想要的字段值
***
*/
orderEntityOP.get().getFw_order_ref().stream().filter(fuwuOrderRefOP -> StringUtils.isEquals(fuwuNo, fuwuOrderRefOP.getFuwu_no())).findFirst().ifPresent(fuwuOrderRefOP -> {
fuwuOrderRefOP.getFw_product_snap_ref().forEach(fuwuProductSnapRefOP -> {
fuwuProductSnapRefOP.getFw_ext_info_ref().stream().filter(fuwuExtInfoRef -> "productJson".equals(fuwuExtInfoRef.getExt_key())).findFirst().ifPresent(fuwuExtInfoRef -> {
EsExtValueBean esExtValueBean = JSONObject.parseObject(fuwuExtInfoRef.getExt_value(), EsExtValueBean.class);
});
});
});