java8特性

Lambda

Lambda是一种函数式编程,可以不严谨的理解为一个函数,是一套关于函数f(x)定义、输入量、输出量的计算方案。除了输入输出以及函数内容,其他的lambda都不关心:

factory = new Factory() {
    @Override
    public Object getObject() {
        return new User();
    }
}

// 只关心 输入 - ()  输出 factory  内容 {}
factory = () -> {
    return new User();
}

函数式编程的主要特点:

  1. 函数是一等公民

  2. 可以赋值给变量

  3. 可以作为其他函数的参数进行传递

  4. 可以作为其他函数的返回值

Lambda语法格式

  1. 格式1

  2. 格式2

其他规则:

  1. 可选的大括号,当函数体只包含一个语句,可以省略大括号

  2. 可选的参数类型声明,编译器可以根据参数值进行推断

  3. 可选的小括号,如果只有一个参数,可以省略小括号

  4. 可选的return关键字,如果函数体只有一个表达式,且运算结果匹配返回类型,return可以省略

Lambda使用前提(函数式接口)

因为在Java中没有函数的概念,只有方法的概念,所有的方法一定从属于一个对象或者Class,所以为了可以表达函数的概念,提供了函数式接口。函数式接口是Java中一种特殊的接口:

  1. 有且只有一个抽象方法的接口是函数式接口

  2. 满足一条件的就是函数式接口,可以通过@FunctionalInterface注解标记,被标记的接口如果不符合函数式接口的条件将会报错

看到函数式接口,就代表定义了一个这样的函数签名

常见的函数式接口

Runnable / Callable

略。

Comparator

略。

Supplier

Supplier即提供实例的供应商。该接口的定义,是获取一个结果,或者说,返回一个指定类型的实例,而且,针对同一个类型,不保证每次返回实例相同。

当我们把一个实例的类型、数据等信息收集好了,就可以交给Supplier接口(通过反射)去完成实现,当需要的时候就通过get()返回那个实例,这就是懒加载。在Spring和JDK中都有这么用过。

应用场景:

  1. 配合Future(J.U.C),把返回的信息封装/设置成具体类型的实例;

  2. 在流操作中,获取源数据(资源文件、管道等)的实例,封装各种buffer的实例等,包括反射获取source;

  3. java.util.stream,用作返回收集、分割、查找、过滤等操作的实例;

  4. 在日志系统中,封装一个“消息提供者”

  5. 在网络编程中,封装二进制数据

  6. 在Spring的实例初始化时,在AbstracBeanDefinition中提供了实例供应商,用于回调生成bean

  7. 常用于设计模式:委托、工厂等

Consumer

对象消费者:

典型应用场景:

BiConsumer

代表连续消费两个入参的操作:

Predicate

谓语,Predicate代表一个断定式子,其函数名为 test

  1. 评估参数里面的表达式

  2. 返回值是一个boolean类型

示例:

提供的其他的default方法:

  1. and, 等同于短路与&&

  2. or,等同于逻辑或||

  3. negate,等同于逻辑非!

  4. isEqual,判断两个对象是否相同,实际是调用Objects.equals方法

注意,上述方法都返回当前Predicate函数对象,所以可以进行链式调用,但是test方法返回true。

Function

BiFunction

接受输入两个参数,返回一个结果

Lambda底层实现

Lambda表达式实际上是匿名内部类的匿名实现。

方法引用

方法引用是对Lambda表达式的再次简化:

方法引用语法格式

方法引用运算符:::

哪儿些方法可以引用?

  1. 类方法:Integer::parseInt

  2. 构造方法:Student::new

  3. 实例方法:System.out::printlnsuper::方法名this::方法名int[]::new 数组方法引用

可以采用引用的前提:

  1. 参数列表相同

  2. 返回值类型兼容

方法引用底层实现

与Lambda的原理一致,本质也是匿名内部类。

Stream

关注做什么,而不是怎么做。

  1. 专注对容器对象的聚合操作

  2. 提供串行/并行两种模式(fork/join框架拆分任务)

  3. 提高编程效率与可读性

Stream常用API

Stream流的API大致可以分为两大类:

  1. 中间操作,可以有零个或多个,打开流,过滤/映射,返回新流

  2. 终结操作,只能有一个的最后的操作,调用终结操作Stream流将会被关闭。

    终结操作也是一种短路操作,可以根据情况中断流处理。

构建Steam

collect 收集

Collector收集器有一个对应的工具类 Collectors,可以返回一些比较常用的收集器:

并行Stream

stream()方法产生的流是串行的,也就是说流中的操作在一个线程中运行。而通过parallelStream()创建的流则是一个并行的流,他的内部是一个ForkJoinPool,处理时拆分处理,最后将结果合并。

获取并行流:

并行流因为基于ForkJoinPool,所以也有线程安全的问题(在流处理函数中访问非线程安全的变量)。个人认为,当数据量处理过慢,或者集合过大,应该优先从其他方面优化(提升执行速度、减少数据量),而不是一定要使用并行流。

使用Optional处理Null

三种构造

何时使用每种构造?

Optional.of(obj):

  1. 明确obj不可能为null

  2. 明确obj为null,并快速抛出异常

Optional.ofNullable(obj):

  1. 不明确obj是否为null,又要对obj进行处理的

存在即返回,无则提供默认值

存在即返回,无则由函数生成

存在则返回, 无则抛出异常

变种,存在则返回,无则抛出空指针异常:

存在才执行, 无则不会执行

map 处理级联数据

有一 user 对象,不知是否为null

如果为null,返回null

如果不为null,返回 name

如果name 为null,返回 null

如果name 不为null, 将name 转换为大写

这种级联的null 处理,需要使用map, map可以嵌套无数层。

flatMap:

flatMapmap 类似,只是参数不同,他的函数参数需要返回 Optional 类型:

总结

一句话小结: 使用 Optional 时尽量不直接调用 Optional.get() 方法, Optional.isPresent() 更应该被视为一个私有方法, 应依赖于其他像 Optional.orElse(), Optional.orElseGet(), Optional.map() 等这样的方法.

新的日期API

JSR-310规范提供一个新的和改进的Java日期与时间API,该规范领导者Stephen Colebourne就是joda-time作者,因此很多环节很像joda-time。

JDK8的新的设计时间日期API,位于java.time下面,并且都是线程安全的:

  1. LocalDate 本地日期

  2. LocalTime 本地时间

  3. LocalDateTime 本地日期时间

  4. DateTimeFormatter 日期时间格式化类

  5. Instant 时间戳

  6. Duration 时间段(两个时间的间隔)

  7. Period 日期段(两个日期的间隔)

  8. ZonedDateTime 具有时区的日期时间

此外,Java中使用的历法是ISO 8601日历系统,也就是公历。平年有365天,闰年有366天。此外,Java8还提供了4套其他的历法,他们分别是:

  1. ThaiBuddhistDate :泰国佛教历

  2. MinguoDate :中华民国历

  3. JapaneseDate :日本历

  4. HijraDate :伊斯兰历

旧版日期时间API缺陷

  1. 日期时间类设计不合理,有java.util.date以及java.sql.Date两个类。其中前者拥有日期和时间,后者仅仅拥有日期。

  2. 日期时间格式化设计不合理,在java.text下。

  3. 非线程安全的,所有的日期类都是可变的。

  4. 时区处理麻烦,日期类并不提供国际化,没有时区的支持。

LocalDate

LocalTime

LocalDateTime

日期时间修改

LocalDateLocalTimeLocalDateTime提供了一系列的with方法,用于根据当前日期修改得到新的日期。注意,他们默认都是不可修改的对象,故他们是线程安全的,修改操作会返回一个新的日期时间对象:

也提供了一系列的plus以及minus方法,用于在当前日期的基础上,加上或者减去指定的时间:

更复杂的情况可以借助时间矫正器 TemporalAdjuster,完成类似的以下的日期时间修改功能:

  1. 获取下个月的第一天

  2. 获取上周三的日期

TemporalAdjuster 提供了大量的默认实现,用于简化常用的日期修改操作。

日期时间比较

日期格式化

在JDK8中,可以通过java.time.format.DateTimeFormatter类进行日期时间格式化于解析:

Instant

表示时间戳,内部保存了从1770年1月1日0时0分以来的秒数以及纳秒数。

Duration 和 Period

DK8提供的用于计算时间日期时间差的工具类。其中

  1. Duration,可以计算 LocalTimeLocalDateTimeInstant的时间差

  2. Period,可以计算LocalDate之间的时间差

时区日期时间类

Java8中,LocalDateLocalTimeLocalDateTime是不带时区的,带时区的提供了三个对应的类,分别为:

  1. ZonedDate

  2. ZonedTime

  3. ZonedDateTime

其中每个时区都对应着一个ID,存储在ZonedId类中。

框架支持

要想在Mybatis、Jackson中使用JSR310,需要添加JSR310的实现。

  1. LocalDate映射数据库中的date类型

  2. LocalTime来映射数据库中的time类型

  3. LocalDateTime字段来映射数据库中的datetime类型

最后更新于

这有帮助吗?