从SpringBoot 2.0.0开始,对于jdk的版本要求从原来的1.7改至1.8,很多人可能对这些新特性完全没有概念。那么对于2014年发布的jdk1.8的特性,你了解多少呢?在实际的编码中,你会想起用到的特性又有哪些?他究竟有哪些优势呢?请跟随我一起揭开它神秘的面纱,(#^.^#)

1.特点简介

  • 速度更快
  • 代码更少(增加了新的语法 Lambda 表达式
  • 强大的 Stream API
  • 便于并行
  • 最大化减少空指针异常 Optional
  • Nashorm引擎,允许在JVM上运行JS应用

其中最为核心的为 Lambda 表达式与Stream API

2.Lambda表达式

Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

2.1示例

匿名内部类方式:

  1. Comparator<Integer> comparable = new Comparator<Integer>() {
  2. @Override
  3. public int compare(Integer x, Integer y) {
  4. return Integer.compare(x, y);
  5. }
  6. };

lambda表达式:

  1. Comparator<Integer> comparable = (x, y) -> Integer.compare(x, y);
  2. ## 如果多个参数的顺序与执行的顺序一致,并且只有一条语句,可以进一步简化
  3. ====>
  4. Comparator<Integer> comparable = Integer::compare;

总结:lambda的本质:作为函数式接口的实例
左边:

  • 参数类型可以省略(lambda表达式运行依赖于上下文环境,是由编译器自动推断的,就是所谓的类型推断)
  • 单个参数可以省略 (),如果只有一句执行语句,可以都省略

image.png

2.函数式接口

  1. 如果一个接口中,只拥有一个抽象方法,那么就是一个函数式接口。我们也可以自定义接口实现。
  2. 在任何接口上使用@FunctionalInterface 注解作为检查是否符合规范使用。
  3. 如果lambda表达式抛出一个受检异常,那么需要在接口上声明这个异常。 ```java @FunctionalInterface //作为检查是否符合规范使用 public interface MyFunction2 {

    public R getValue(T t1, T t2);

}

  1. @Test
  2. public void test3(){
  3. op(100L, 200L, (x, y) -> x + y);
  4. op(100L, 200L, (x, y) -> x * y);
  5. }
  6. //需求:对于两个 Long 型数据进行处理
  7. public void op(Long l1, Long l2, MyFunction2<Long, Long> mf){
  8. System.out.println(mf.getValue(l1, l2));
  9. }
  1. <a name="nhPv9"></a>
  2. ## 2.1JAVA内置四大接口函数
  3. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/2420521/1614220822178-b22697bd-65c8-4b4a-98af-4d2e178a4f77.png#align=left&display=inline&height=633&margin=%5Bobject%20Object%5D&name=image.png&originHeight=633&originWidth=1107&size=452908&status=done&style=none&width=1107)
  4. <a name="zlFxV"></a>
  5. # 3.方法引用和构造器引用
  6. <a name="l3X7A"></a>
  7. ## 3.1方法引用
  8. 当要传递给Lambda体的操作,已经有实现的方法了,可以用方法引用。注意:实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致,包括顺序。<br />主要有以下三种使用情况:
  9. - 对象::实例方法
  10. - 类::静态方法
  11. - 类::实例方法
  12. **_类::静态方法_**
  13. ```java
  14. MyFunction001 myFunction001 = str -> System.out.println(str);
  15. 转化为=====>
  16. MyFunction001 myFunction001 = System.out::println;
  17. =====================================
  18. 如果是以下形式,则不能使用
  19. MyFunction001 myFunction001 = str -> System.out.println(str+"");

对象::实例方法

  1. Comparator<Integer> comparable = (x, y) -> Integer.compare(x, y);
  2. int compare = comparable.compare(6, 4);
  3. 转化为=====>
  4. Comparator<Integer> comparable = Integer::compare;
  5. int compare = comparable.compare(6, 4);

类::实例方法
注意:当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引
用方法的第二个参数(或无参数)时:ClassName::methodName

  1. MyFunction001 myFunction001 = (str1, str2) -> str1.equals(str2);
  2. myFunction001.test("aa","bb");
  3. 转化为=====>
  4. //标记为String是因为类型推断为String类
  5. MyFunction001 myFunction001 = String::equals;
  6. myFunction001.test("aa","bb");

3.2构造器引用

image.png

  1. Supplier<Employee> supplier = new Supplier<Employee>() {
  2. @Override
  3. public Employee get() {
  4. return new Employee();
  5. }
  6. };
  7. 或者
  8. MyFunction001<Employee> supplier = (name, age) -> new Employee(name, age);
  9. 均可以转化为=====>
  10. Supplier<Employee> supplier = Employee::new;

3.3数组引用

格式:type[]::new

  1. Function<Integer,Integer[]> function = new Function<Integer, Integer[]>() {
  2. @Override
  3. public Integer[] apply(Integer integer) {
  4. return new Integer[integer];
  5. }
  6. };
  7. 转化==>
  8. Function<Integer,Integer[]> function = integer -> new Integer[integer];
  9. 转化==>
  10. Function<Integer,Integer[]> function = Integer[]::new;

4.Stream API

4.1简介

Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API(java.util.stream.*)。Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
目的是写出高效、简洁的代码。

为什么使用 Stream API?
实际项目过程中,除了多数来自于Mysql等关系型数据库的数据以外,还会使用redis、MongDB等NoSql数据库。而这些数据需要Java来进行处理。
Stream和Collection集合的区别:
Collection是一种静态的内存数据结构,而Stream是有关计算的。前者面向内存进行存储,而后者面向CPU进行计算。

流(Stream) 到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

4.2Stream 操作三个步骤

创建Stream:
一个数据源(如:集合或者数组)
中间操作:
一个中间操作链,对上游数据进行处理然后交给下游。
终止操作(终端操作)
一旦执行终止操作,就会执行中间操作链得到结果,操作完成后就结束。

4.3Stream创建方式

  1. //方式一:通过集合
  2. List<Employee> emps = Arrays.asList(
  3. new Employee(101, "张三", 18, 9999.99),
  4. new Employee(102, "李四", 59, 6666.66),
  5. new Employee(103, "王五", 28, 3333.33),
  6. new Employee(104, "赵六", 8, 7777.77),
  7. new Employee(105, "田七", 38, 5555.55)
  8. );
  9. //返回一个顺序流
  10. Stream<Employee> stream = emps.stream();
  11. //返回一个并行流
  12. Stream<Employee> employeeStream = emps.parallelStream();
  13. //方式二:通过数组
  14. int[] arr = new int[]{1, 2, 3, 1, 4};
  15. IntStream stream1 = Arrays.stream(arr);
  16. //方式三:通过 of 方法
  17. Stream<Object> stream2 = Stream.of(1, 2, 3, 4, 5, "66");
  18. //方式四:创建无限流,自定义一些数据
  19. Stream.iterate(0, integer -> integer + 2)
  20. .limit(10)
  21. .forEach(System.out::println);

4.3Stream的中间操作

  • 筛选和切片

多个中间操作可以连接起来形成流水链,只有触发了终止操作,中间操作一次性处理完成,称为“惰性求值”。

filter——接收 Lambda , 从流中排除某些元素。
limit——截断流,使其元素不超过给定数量。
skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
distinct——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
Stream API 和 传统遍历操作区别

  1. //内部迭代:迭代操作 Stream API 内部完成
  2. @Test
  3. public void test2(){
  4. //所有的中间操作不会做任何的处理
  5. Stream<Employee> stream = emps.stream()
  6. .filter((e) -> {
  7. System.out.println("测试中间操作");
  8. return e.getAge() <= 35;
  9. });
  10. //只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
  11. stream.forEach(System.out::println);
  12. }
  13. //外部迭代
  14. @Test
  15. public void test3(){
  16. Iterator<Employee> it = emps.iterator();
  17. while(it.hasNext()){
  18. System.out.println(it.next());
  19. }
  20. }
  • 映射

    1. 方法 | 描述
    2. -|-
    3. map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
    4. mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream |
    5. mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream |
    6. mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。|
    7. flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。|
  • 排序

sorted():产生一个新流,按照自然排序。
sorted(Comparator comp):产生一个新流,按照比较器排序。

  1. emps.stream()
  2. .sorted((x, y) -> {
  3. if(x.getAge() == y.getAge()){
  4. return x.getName().compareTo(y.getName());
  5. }else{
  6. return Integer.compare(x.getAge(), y.getAge());
  7. }
  8. }).forEach(System.out::println);

4.3Stream的终止操作

终端操作会从流的流水线中生成结果,结果可以是任何不是流的值,例如 list、set、void。

查找与匹配

  • allMatch——检查是否匹配所有元素
  • anyMatch——检查是否至少匹配一个元素
  • noneMatch——检查是否没有匹配的元素
  • findFirst——返回第一个元素
  • findAny——返回当前流中的任意元素
  • count——返回流中元素的总个数
  • max——返回流中最大值
  • min——返回流中最小值
  • forEach——内部迭代

注意:流进行了终止操作后,不能再进行操作了

  1. @Test
  2. public void test1(){
  3. boolean bl = emps.stream()
  4. .allMatch((e) -> e.getStatus().equals(Status.BUSY));
  5. System.out.println(bl);
  6. boolean bl1 = emps.stream()
  7. .anyMatch((e) -> e.getStatus().equals(Status.BUSY));
  8. System.out.println(bl1);
  9. boolean bl2 = emps.stream()
  10. .noneMatch((e) -> e.getStatus().equals(Status.BUSY));
  11. System.out.println(bl2);
  12. }
  13. @Test
  14. public void test2(){
  15. Optional<Employee> op = emps.stream()
  16. .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
  17. .findFirst();
  18. System.out.println(op.get());
  19. System.out.println("--------------------------------");
  20. Optional<Employee> op2 = emps.parallelStream()
  21. .filter((e) -> e.getStatus().equals(Status.FREE))
  22. .findAny();
  23. System.out.println(op2.get());
  24. }
  25. //注意:流进行了终止操作后,不能再次使用
  26. @Test
  27. public void test4(){
  28. Stream<Employee> stream = emps.stream()
  29. .filter((e) -> e.getStatus().equals(Status.FREE));
  30. long count = stream.count();
  31. //下面代码无效,会报错
  32. stream.map(Employee::getSalary)
  33. .max(Double::compare);
  34. }

归约

reduce(T iden,BinaryOperator b)——可以将流的元素反复结合起来,得到一个值,返回 T。
reduce(BinaryOperator b)——可以将流的元素反复结合起来,得到一个值,返回 Optional

  1. @Test
  2. public void test1(){
  3. List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
  4. Integer sum = list.stream()
  5. .reduce(0, (x, y) -> x + y);
  6. System.out.println(sum);
  7. System.out.println("----------------------------------------");
  8. Optional<Double> op = emps.stream()
  9. .map(Employee::getSalary)
  10. .reduce(Double::sum);
  11. System.out.println(op.get());
  12. }

收集

collect(Collecttor c)——接收一个Collection接口的实现,用于给Stream做元素汇总方法。

  1. @Test
  2. public void test4(){
  3. Optional<Double> max = emps.stream()
  4. .map(Employee::getSalary)
  5. .collect(Collectors.maxBy(Double::compare));
  6. System.out.println(max.get());
  7. Optional<Employee> op = emps.stream()
  8. .collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
  9. System.out.println(op.get());
  10. Double sum = emps.stream()
  11. .collect(Collectors.summingDouble(Employee::getSalary));
  12. System.out.println(sum);
  13. Double avg = emps.stream()
  14. .collect(Collectors.averagingDouble(Employee::getSalary));
  15. System.out.println(avg);
  16. Long count = emps.stream()
  17. .collect(Collectors.counting());
  18. System.out.println(count);
  19. System.out.println("--------------------------------------------");
  20. DoubleSummaryStatistics dss = emps.stream()
  21. .collect(Collectors.summarizingDouble(Employee::getSalary));
  22. System.out.println(dss.getMax());
  23. }
  24. //分组
  25. @Test
  26. public void test5(){
  27. Map<Status, List<Employee>> map = emps.stream()
  28. .collect(Collectors.groupingBy(Employee::getStatus));
  29. System.out.println(map);
  30. }
  31. //多级分组
  32. @Test
  33. public void test6(){
  34. Map<Status, Map<String, List<Employee>>> map = emps.stream()
  35. .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
  36. if(e.getAge() >= 60)
  37. return "老年";
  38. else if(e.getAge() >= 35)
  39. return "中年";
  40. else
  41. return "成年";
  42. })));
  43. System.out.println(map);
  44. }
  45. //分区
  46. @Test
  47. public void test7(){
  48. Map<Boolean, List<Employee>> map = emps.stream()
  49. .collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
  50. System.out.println(map);
  51. }
  52. //
  53. @Test
  54. public void test8(){
  55. String str = emps.stream()
  56. .map(Employee::getName)
  57. .collect(Collectors.joining("," , "----", "----"));
  58. System.out.println(str);
  59. }
  60. @Test
  61. public void test9(){
  62. Optional<Double> sum = emps.stream()
  63. .map(Employee::getSalary)
  64. .collect(Collectors.reducing(Double::sum));
  65. System.out.println(sum.get());
  66. }

5.Optional类

Optional 容器类:用于尽量避免空指针异常

  1. Optional.of(T t) : 创建一个 Optional 实例
  2. Optional.empty() : 创建一个空的 Optional 实例
  3. Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
  4. isPresent() : 判断是否包含值
  5. orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
  6. orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
  7. map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回
  8. Optional.empty()
  9. flatMap(Function mapper):与 map 类似,要求返回值必须是Optional

    1. public String getGodnessName2(Optional<NewMan> man){
    2. return man.orElse(new NewMan())
    3. .getGodness()
    4. .orElse(new Godness("老师"))
    5. .getName();
    6. @Test
    7. public void test4(){
    8. Optional<Employee> op = Optional.of(new Employee(101, "张三", 18, 9999.99));
    9. Optional<String> op2 = op.map(Employee::getName);
    10. System.out.println(op2.get());
    11. Optional<String> op3 = op.flatMap((e) -> Optional.of(e.getName()));
    12. System.out.println(op3.get());
    13. }
    14. @Test
    15. public void test3(){
    16. Optional<Employee> op = Optional.ofNullable(new Employee());
    17. if(op.isPresent()){
    18. System.out.println(op.get());
    19. }
    20. Employee emp = op.orElse(new Employee("张三"));
    21. System.out.println(emp);
    22. Employee emp2 = op.orElseGet(() -> new Employee());
    23. System.out.println(emp2);
    24. }

    6.重复注解与类型注解

    image.png

    7.并行流和串行流

    并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

    1. //返回一个顺序流
    2. Stream<Employee> stream = emps.stream();
    3. //尝试返回一个可能存在的并行流,允许返回顺序流
    4. Stream<Employee> employeeStream = emps.parallelStream();

    8.接口中的默认方法和静态方法

    8.1默认方法

    JAVA8允许接口中包含有具体实现的方法,该方法称为默认方法。使用 default关键字修饰
    image.png

    接口默认方法的”类优先”原则

    若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时。

  • 选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
  • 接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突。

image.png

8.2静态方法

java8中,接口中允许添加静态方法。
image.png

9.新时间日期API

这一段没有细看,直接贴图了,还望谅解!

image.png
image.png
image.pngimage.png
image.png
image.png
image.png
image.png