Java8新特性
函数式接口
概念
- 有且只有一个抽象方法的接口,称之为函数式接口
 - 接口中可以包含其他的方法(默认,静态,私有),但是只能有一个抽象方法。
 @FunctionalInterface注解- 检测接口是否是一个函数式接口
- 是;编译成功
 - 否;编译失败(接口中没有抽象方法或抽象方法的个数大于1)
 
 - 格式;
1
2
3
4
public interface 接口名{
抽象方法
} 
- 检测接口是否是一个函数式接口
 使用;一般可以作为方法的参数和返回值类型
函数式编程
Lambda的延迟执行
- Lambda特点;延迟执行
 - 作用;提升性能
 
常用函数式接口
Supplier接口
java.util.function.Supplier<T>;接口仅包含一个无参的方法;T get()。用来获取一个泛型参数指定类型的对象数据。- 生产型接口;指定接口的泛型是什么类型,那么接口中的get方法就会产生什么类型的数据
 
Consumer接口
java.util.function.Consumer<T>;接口包含抽象方法void accept(T t),意为消费一个指定泛型数据- 消费型接口,泛型执行什么类型,就可以使用accept方法消费什么类型的数据,至于具体怎么消费,需要定义计算
 - 默认方法;
addThen- 需要两个Consumer接口,可以把两个Consumer接口组合到一起,在对数据进行消费
 - 源码;
1
2
3
4default Consumer<T andThen(Consumer<? super T> after){
Objects.requirNonNull(after);
return (T t)->{accept(t);after.accept(t);};
} 
 
Predicate接口
java.util.function.Predicate<T>接口;对某种数据类型的数据进行判断,结果返回一个Boolean值- 抽象方法;
boolean test(T t);用来对指定数据类型数据进行判断的方法 默认方法;
add;与1
2
3
4default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) ‐> test(t) && other.test(t);
}or;或1
2
3
4default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) ‐> test(t) || other.test(t);
}negate;非1
2
3default Predicate<T> negate() {
return (t) ‐> !test(t);
}
Function接口
java.util.function.Function<T,R>;接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件- 抽象方法;
R apply(T t);根据类型T的参数获取类型R的的结果(一般用于类型转换) - 默认方法;
andThen
 
Stream流式思想
- JDK1.8之后
 - 关注做什么,不关注怎么做
 - “Stream流”其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其地址值)
 - Stream(流)是一个来自数据源的元素队列
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
 - 数据源流的来源。 可以是集合,数组等。
 
 - Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
 - 内部迭代: 以前对集合遍历都是通过Iterator或者增强for的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式,流可以直接调用遍历方法。
 
 - 使用步骤
- 获取数据源
 - 数据转换
 - 执行操作获取想要的结果
 
 - 注意;
 - 两种方法
 default Stream<E> stream()
根据Stream接口获取流
static <T> Stream<T> of(T...values);参数是一个可变参数,可以传递一个数组
常用方法
- 延迟方法;返回值类型仍然是
Stream接口自身类型的方法,因此支持链式调用。 - 终结方法;返回值类型不再是
Stream接口自身类型的方法,因此不再支持类似StringBuilder那样的链式调用逐一处理;foreach
 void forEach(Consumer<? super T> action);该方法接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理- forEach方法,用来遍历流中的数据,是一个终结方法,遍历之后就不能继续调用Stream流中的其他方法
过滤;filter
 Stream<T> filter(Predicate<? super T> predicate);可以通过filter方法将一个流转换为另一个子集流映射;map
<R> Stream<R> map(Function<? super T, ? extends R> mapper);;将流中的元素映射到另一个流中,可以使用map方法统计个数;count
long count();用于统计Stream流中元素的个数- count方法是一个终结方法,返回值是一个long类型的整数,所有不能再继续调用Stream流中的其他方法
取用前几个;limit
 Stream<T> limit(long maxSize);;用于截取流中的元素- limit是一个延迟方法,只是对流中的元素进行截取,返回是一个新的流,所以可以继续调用Stream流中的其他方法
跳过前几个;skip
 Stream<T> skip(long n);;用于跳过元素- 如果流的当前长度大于n,则跳过前n个,否则会得到一个长度为0的空流
组合;concat
 static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b);将两个流合并为一个流- 这是一个静态方法,与String中的concat方法不同
 
方法引用
- 方法引用符:双冒号
::为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者 - 语义;
- Lambda表达式写法: 
s -> System.out.println(s);拿到参数之后经Lambda之手,继而传递给 System.out.println 方法去处理。 - 方法引用写法:
System.out::println;直接让System.out中的println方法来取代Lambda。两种写法的执行效果完全一样,而第二种方法引用的写法复用了已有方案,更加简洁。注:Lambda中传递的参数一定是方法引用中的那个方法可以接收的类型,否则会抛出异常
 
 - Lambda表达式写法: 
 - 通过对象名引用成员方法
- 通过对象名引用成员方法,使用前提是对象名已经存在,成员方法也是已经存在的
 
 - 通过类名称引用静态方法
 - 通过super引用成员方法
 - 通过this引用成员方法、
 - 类的构造器引用
 - 数组的构造器引用