JDK8新特性

JDK8的改进

  • 速度更快

  • 代码更少(增加了新的语法:Lambda表达式)

  • 引入强大的 Stream APl

  • 便于并行

  • 最大化减少空指针异常:Optional

  • Nashorn 引擎,允许在JVM上运行 JS 应用

  • 并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。

  • Java 8中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换

JDK8新特性汇总

  • Lambda 表达式 − Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

  • 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

  • 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。

  • 新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。

  • Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。

  • Date Time API − 加强对日期与时间的处理。

  • Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。

  • Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。

1.Lambda 表达式

  • 概述:Lambda是Java 8中添加的新特性, 说白了, Lambda就是一个匿名函数.使用Lambda表达式可以对一个接口进行非常简洁的实现.虽然可以使用Lambda表达式对某些接口进行简单的实现, 但是并不是所有的接口都可以用Lambda表达式来实现. 要求接口中定义的必须要实现的抽象方法只能是一个.如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。

  • 语法格式
    语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 ,读作(goes to)。

  • Lambda 基础语法
    我们这里给出六个接口,后文的全部操作都利用这六个接口来进行阐述

@FunctionalInterface
public interface LambdaDemo {
    /**
     * 无参无返回值
     */
    void eat();
}

@FunctionalInterface
public interface LambdaDemo2 {
    /**
     * 一个参数无返回值
     *
     * @param a
     */
    void eat(int a);
}

@FunctionalInterface
public interface LambdaDemo3 {
    /**
     * 多个参数无返回值
     *
     * @param a
     * @param b
     * @param c
     */
    void eat(int a, String b, int c);
}

@FunctionalInterface
public interface LambdaDemo4 {

    /**
     * 无参有返回值
     *
     * @return
     */
    String eat();
}

@FunctionalInterface
public interface LambdaDemo5 {

    /**
     * 一个参数有返回值
     *
     * @param a
     * @return
     */
    String eat(int a);
}

@FunctionalInterface
public interface LambdaDemo6 {

    /**
     * 多个参数有返回值
     *
     * @param a
     * @param b
     * @param c
     * @return
     */
    String eat(int a, String b, int c);
}


下面是使用lambda写法对这些接口方法的调用

public class Test1 {
    public static void main(String[] args) {
        // 无参无返回值
        LambdaDemo lambdaDemo = () -> {
            System.out.println("lambdaDemo");
        };
        lambdaDemo.eat();

        // 一个参数无返回值
        LambdaDemo2 lambdaDemo2 = (int a) -> {
            System.out.println("lambdaDemo2" + "," + a);
        };
        lambdaDemo2.eat(100);

        // 多个参数无返回值
        LambdaDemo3 lambdaDemo3 = (int a, String b, int c) -> {
            System.out.println("lambdaDemo3:" + a + "," + b + "," + c);
        };
        lambdaDemo3.eat(1, "test", 2);

        // 无参有返回值
        LambdaDemo4 lambdaDemo4 = () -> {
            System.out.println("lambdaDemo4");
            return "test";
        };
        System.out.println(lambdaDemo4.eat());

        // 一个参数有返回值
        LambdaDemo5 lambdaDemo5 = (int a) -> {
            System.out.println("lambdaDemo5:" + a);
            return "test:" + a;
        };
        System.out.println(lambdaDemo5.eat(100));

        // 多个参数有返回值
        LambdaDemo6 lambdaDemo6 = (int a, String b, int c) -> {
            System.out.println("lambdaDemo6:" + a + "," + b + "," + c);
            return "test:" + a + "," + b + "," + c;
        };
        System.out.println(lambdaDemo6.eat(100, "test", 100));
    }
}

上述写法可以简化:

  • 1.简化参数类型,可以不写参数类型,但是必须所有参数都不写
  • 2.简化参数小括号,如果只有一个参数则可以省略参数小括号
  • 3.简化方法体大括号,如果方法体只有一条语句,则可以省略方法体大括号
  • 4.如果方法体只有一条语句,并且是 return 语句,则可以省略方法体大括号

2.函数式接口

  • 概述:只包含一个抽象方法的接口,称为函数式接口,可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。在 java.util.function 包下定义了Java 8的丰富的函数式接口。

自定义函数式接口

@FunctionalInterface
public interface LambdaDemo6 {

    /**
     * 多个参数有返回值
     *
     * @param a
     * @param b
     * @param c
     * @return
     */
    String eat(int a, String b, int c);
}
  • java四大核心内置函数式接口
    1.Supplier 提供者,如其名,不接收参数,返回T类型对象
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}
  1. Consumer 消费者,如其名,接收一个参数对象T, 不返回结果,主要针对处理部分业务场景
@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    // andThen 处理两步 接收一个Consumer对象 先处理accept 在处理 consumer里面的accept 相当于处理两步
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

3.Function 方法,如其名,接收一个参数T 返回一个结果R

@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
}
    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     *
     * @see #compose(Function)
     */
	// 如其名 andThen 在执行apply之后 对返回结果 R V,返回V 
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    /** 
     * Returns a function that always returns its input argument.
     *
     * @param <T> the type of the input and output objects to the function
     * @return a function that always returns its input argument
     */
	// 输入 一个对象 并返回它
    static <T> Function<T, T> identity() {
        return t -> t;
    }

4.Predicate 预测,如其名,接收一个参数T 返回boolean值

@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

}

3.方法引用

  • 概述:方法引用可以看做是 Lambda 表达式深层次的表达。换句话说,方法引用就是 Lambda 表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法。 当要传递给 Lambda 体的操作,已经实现的方法了,可以使用方法引用!
  • 使用格式
    类(或对象) :: 方法名
  • 使用情况
    情况1 对象 :: 非静态方法
    情况2 类 :: 静态方法
    情况3 类 :: 非静态方法
  • 使用要求
    要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同!(针对于情况1和情况2)
    当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法的参数(或无参数)时:ClassName::methodName(针对于情况3)
public class Test2 {
    public static void main(String[] args) {
        // 使用Lambda表达
        Consumer<String> con1 = str -> System.out.println(str);
        con1.accept("中国");

        // 使用方法引用
        PrintStream ps = System.out;
        Consumer con2 = ps::println;
        con2.accept("China");
    }
}

4.默认方法

  • 概述:简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。我们只需在方法名前面加个default关键字即可实现默认方法。
  • 代码示例
public interface Demo {
    /**
     * 接口默认方法
     */
    default void test() {
        System.out.println("我是接口中默认方法");
    }
}
  • 一个接口有默认方法,考虑这样的情况,一个类实现了多个接口,且这些接口有相同的默认方法,解决方案:
    1.第一个解决方案是创建自己的默认方法,来覆盖重写接口的默认方法
    2.第二种解决方案可以使用 super 来调用指定接口的默认方法

  • 静态默认方法 Java 8 的另一个特性是接口可以声明(并且可以提供实现)静态方法。例如:

public interface Demo {
    /**
     * 静态默认方法
     */
    static void test() {
        System.out.println("静态默认方法");
    }
}

5.Stream

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果

  • 使用注意点
    ① Stream 自己不会存储元素。
    ② Stream 不会改变源对象。相反,他们会返回一个持有结果的新 Stream。
    ③ Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

  • 创建stream流
    1.通过集合
    Java 8的 Collection 接口被扩展,提供了两个获取流的方法:
    default Stream<E> stream() : 返回一个顺序流
    default Stream<E> parallelStream() : 返回一个并行流

    2.通过数组
    Java 8中的 Arrays 的静态方法 stream() 可以获取数组流
    调用 Arrays 类的 static<T> Stream<T> stream(T[] array): 返回一个流

    3.通过Stream的of()方法
    可以调用Stream类静态方法of(),通过显示值创建一个流。可以用于接收任意数量的参数

    4.创建无限流
    迭代: public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
    生成: public static<T> Stream<T> generate(Supplier<T> s)

public class Demo1 {
    public static void main(String[] args) {
        // 创建stream流 1.通过集合
        ArrayList<String> list = new ArrayList<>();
        // default Stream\<E> stream() : 返回一个顺序流
        Stream<String> stream = list.stream();
        // default Stream\<E> parallelStream() : 返回一个并行流
        Stream<String> stream1 = list.parallelStream();

        //  2.通过数组
        int[] arr = {1, 2, 3};
        IntStream stream2 = Arrays.stream(arr);

        //  3.通过Stream的of()方法
        Stream<Integer> integerStream = Stream.of(1);

        //  4.创建无限流
        //  迭代: public static\<T> Stream\<T> iterate(final T seed, final UnaryOperator\<T> f)
        Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
        //  生成: public static\<T> Stream\<T> generate(Supplier\<T> s)
        Stream.generate(Math::random).limit(10).forEach(System.out::println);

    }
}
  • 中间操作
    多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为惰性求值。
    1.filter:接收lambda排除掉部分元素
    2.distinct:去重
    3.limit:数量大小限制
    4.skip:跳过
    5.map:映射
    6.sorted:排序
public class Demo2 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        // 过滤
        list.stream().filter(e -> e > 2).forEach(System.out::println);
        // 去重
        list.stream().distinct().forEach(System.out::println);
        // 限制数量
        list.stream().limit(2).forEach(System.out::println);
        // 跳过
        list.stream().skip(2).forEach(System.out::println);
        // 映射
        list.stream().map(e -> e = 3).forEach(System.out::println);
        // 排序
        list.stream().sorted().forEach(System.out::println);

    }
}

  • 终止操作
    终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、 Integer,甚至是 void
    流进行了终止操作后,不能再次使用。
    1.findFirst返回第一个元素
    2.findAny返回当前流中任何元素
    3.count返回流中元素个数
    4.max返回流中最大值
    5.min 返回流中最小值
    6.foreach内部迭代
    7.allMatch检查是否匹配所有元素
public class Demo3 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(5);
        // 1.findFirst返回第一个元素
        Optional<Integer> first = list.stream().findFirst();
        // 2.findAny返回当前流中任何元素
        Optional<Integer> any = list.stream().findAny();
        // 3.count返回流中元素个数
        long count = list.stream().count();
        // 4.max返回流中最大值
        Optional<Integer> max = list.stream().max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        // 5.min 返回流中最小值
        Optional<Integer> min = list.stream().min(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return 0;
            }
        });
        // 6.foreach内部迭代
        list.stream().forEach(System.out::println);
        // 7.allMatch检查是否匹配所有元素
        boolean b = list.stream().allMatch(e -> e > 2);
    }
}

  • 收集collect
public class Demo4 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(5);
        List<Integer> collect = list.stream().filter(e -> e > 2).collect(Collectors.toList());
    }
}

6.Optional 类的使用

1.概述:为了解决 java 中的空指针问题而生!是一个容器类,它可以保存类型 T 的值,代表这个值存在。或者仅仅保存 null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。

2.Optional 类提供了很多方法,可以不用再现实的进行空值检验.
创建 Optional 类对象的方法
Optional.of(T t) : 创建一个 Optional 实例,t 必须非空;
Optional.empty() : 创建一个空的 Optional 实例
Optional.ofNullable(T t):t 可以为 null

判断Optional容器是否包含对象
boolean isPresent():判断是否包含对象
void ifPresent(Consumer<? super T> consumer):如果有值,就执行 Consumer 接口的实现代码,并且该值会作为参数传给它

获取 Optional 容器的对象
T get():如果调用对象包含值,返回该值,否则抛异常

T orElse(T other):如果有值则将其返回,否则返回指定的 other 对象

T orElseGet(Supplier<? extends t> other):如果有值则将其返回,否则返回由 Supplier 接口实现提供的对象。

T orElseThrow(Supplier<? extends X> exceptionSupplier):如果有值则将其返回,否则抛出由 Supplier 接口实现提供的异常。

  • 搭配使用
    of() 和 get() 方法搭配使用,明确对象非空
    ofNullable() 和 orElse() 搭配使用,不确定对象非空
Last Updated:
Contributors: xt