Java8

Oracle JDK是基于Open JDK源代码的商业版本,要学习Java新技术可以去OpenJDK官网学习。

Lambda表达式介绍

  • 匿名内部类存在的问题

    • new Thread(new  Runnable() {
          @Override
          public void run() {
              // ...
          }
      }).start();
      
    • 匿名内部类做了哪些事

      • 定义了一个没有名字的类
      • 实现了Runnable接口
      • 创建了这个类的对象
    • 其实我们最关注的是run方法和里面要执行的代码,使用匿名内部类语法是很冗余的

    • Lambda体现的是函数式编程思想,只需要将要执行的代码放到函数中

    • Lambda就是一个匿名函数,我们只需要将要执行的代码放到Lambda表达式中即可

  • 体验Lambda表达式

    • new Thread(() -> {
          //...
      }).start();
      
    • 好处:

      • 简化匿名内部类,让代码更加精简

Lambda的标准格式

  • Lambda表达式是一个匿名函数,而函数相当于Java中的方法

    • (参数列表) -> {
          
      }
      
    • ():参数列表

    • {}:方法体

    • ->:没有实际含义,起到连接的作用

  • 小结:

    • 以后看到方法的参数是接口就可以考虑使用Lambda表达式
    • 可以这么认为:Lambda表达式就是对接口中抽象方法的重写
    • 不是所有匿名内部类都能用Lambda来替换

Lambda的实现原理

  • 匿名内部类在编译的时候会形成一个.class文件
  • Lambda在程序运行时会形成一个类
    • 在类中新增一个方法,这个方法的方法体就是Lambda表达式中的代码
      • image-20230113102826092
    • 还会形成一个匿名内部类,实现接口,重写抽象方法
    • 在接口的重写方法中会调用新生成的方法
      • image-20230113103632313
  • 理解:Lambda表达式就是对抽象方法的重写

Lambda省略格式

// 省略前
(int a) -> {
    return new Person();
}
// 省略后
a -> new Person()
  • 小括号内参数的类型可以省略
  • 小括号内有且仅有一个参数,小括号可以省略
  • 大括号内有且仅有一个语句,可以同时省略大括号、return关键字及分号

Lambda的前提条件

  • 方法的参数或局部变量的类型必须为接口才能使用Lambda
  • 接口中有且只有一个抽象方法
    • 只有一个抽象方法的接口称为函数式接口,我们就能使用Lambda
    • @FunctionalInterface:检测这个接口是否只有一个抽象方法
      • image-20230113111630307

Lambda和匿名内部类对比

  • 所需的类型不一样
    • 匿名内部类:需要的类型可以是类、抽象类、接口
    • Lambda:需要的类型必须是接口
  • 抽象方法的数量不一样
    • 匿名内部类:数量随意
    • Lambda:只能有一个
  • 实现原理不同
    • 匿名内部类:编译后形成class文件
    • Lambda:运行时动态生成class

JDK8接口新增的两个方法

  • JDK8以前

    • interface 接口名 {
          静态常量;
          抽象方法;
      }
      
  • JDK8对接口的增强,接口还可以有默认方法静态方法

  • JDK8

    • interface 接口名 {
          静态常量;
          抽象方法;
          默认方法;
          静态方法;
      }
      
  • 默认方法

    • image-20230113145429943

      • 因此JDK8时为接口新增了默认方法
      • 接口中的默认方法实现类不必重写,可以直接使用,实现类也可以根据需要重写
    • 默认方法的定义格式

      • interface 接口名 {
            修饰符 default 返回值类型 方法名() {
                //...
            }
        }
        
    • 默认方法的使用

      • 实现类直接调用接口默认方法
      • 实现类重写接口默认方法
  • 静态方法

    • 为了方便接口扩展,JDK8为接口新增了静态方法

    • 静态方法的定义格式

      • interface 接口名 {
            修饰符 static 返回值类型 方法名() {
                //...
            }
        }
        
    • 静态方法的使用

      • 接口静态方法实现类不会继承、不能重写?
      • 使用:接口名.静态方法名()
  • 区别

    • 默认方法通过实例调用,静态方法通过接口名调用
    • 默认方法可以被继承,实现类可以直接使用接口默认方法,也可以重写接口默认方法
    • 静态方法不能被继承,实现类不能重写接口静态方法,只能通过接口名调用

常用内置函数式接口

  • 在 java.util.function包中

  • Supplier接口

    • public interface Supplier<T> {
      	T get();
      }
      
  • Consumer接口

    • public interface Consumer<T> {
          void accept(T t);
      }
      
  • Function接口

    • public interface Function<T, R> {
          R apply(T t);
      }
      
  • Predicate接口

    • public interface Predicate<T> {
          boolean test(T t);
      }
      

方法引用格式

  • 符号表示: ::
  • 符号说明:双冒号为方法引用运算符,而它所在表达式被称为方法引用
  • 应用场景:如果Lambda所要实现的方案,已经有其他方法存在相同方案,那么则可以使用方法引用

对象名::引用成员方法

image-20230117144952979

  • 注意事项
    • 被引用的方法,参数要和接口中抽象方法的参数一样
    • 当接口的抽象方法有返回值时,被引用的方法也必须有返回值

类名::引用静态方法

image-20230117150737718

类名::引用实例方法

image-20230117151817403

  • 注意:
    • 实际上会将第一个参数作为方法的调用者
    • 类名:: 中的类名为第一个参数的类

类名::new引用构造器

image-20230124235145403

数组::new 引用数组构造器

image-20230124235719176

Stream流式思想概述

  • 与IO流没有任何关系
  • image-20230129145150006

获取Stream的两种方式

  • 根据Collection获取流

    • Collection接口中有一个默认方法:default Stream stream()

    • Stream<String> stream1 = list.stream();
      
      Stream<String> stream2 = set.stream();
      
      Stream<String> stream3 = map.keySet().stream();
      Stream<String> stream4 = map.values().stream();
      Stream<Map.Entry<String, String>> stream5 = map.entrySet().stream();
      
  • Stream中静态方法of获取流

    • public static<T> Stream<T> of(T... values) {
          return Arrays.stream(values);
      }
      
    • Stream<String> stream6 = Stream.of("aa", "bb", "cc");
      
      String strs = {"aa", "bb", "cc"};
      Stream<String> stream7 = Stream.of(strs);
      
      //基本数据类型不行,会将整个数组看作一个元素操作
      int[] arr = {11, 22, 33};
      Stream<int []> stream8 = Stream.of(arr);
      

Stream常用方法和注意事项

  • 终结方法

    • 返回值不再是Stream的方法
  • 非终结方法(函数拼接方法)

    • 返回Stream
  • 注意事项

    • Stream只能操作一次
    • Stream方法返回的是新的流
    • Stream不调用终结方法,中间的操作不会执行
  • forEach

  • count

  • filter:返回值为true要,不是true不要

  • limit

  • skip:跳过前几个数据

  • map:映射,将一种类型的流转换为另一种类型

  • sorted

  • distinct:过滤重复数据

    • 自定义类型,重写hashCode和equils
  • match:是否匹配指定条件

    • allMatch:匹配所有元素,所有元素都需要满足条件
    • anyMatch:匹配某个元素,只要有其中一个元素满足条件即可
    • noneMatch:匹配所有元素,所有元素都不满足条件
  • find

    • findFirst
    • findAny
  • max和min

    • 指定排序规则
    • max取排序后最后一个值
    • min取排序后第一个值
  • reduce:将数据归纳总结得到一个数据

    • T reduce(T identity, BinaryOperator<T> accumulator);
      // T identity: 默认值
      // BinaryOperator<T> accumulator: 对数据进行处理的方式 
      
    • int reduce = Stream.of(4, 5, 3, 9).reduce(0, (x, y) -> x + y);
      
      • 第一次,将某人之赋值给x,取出集合第一元素赋值给y
      • 第二次,将上次返回的结果赋值x,取出下个元素赋值给y
    • 可以用于获取最大值

      • (0, (x, y) -> {
            return x > y ? x : y;
        })
        
posted @ 2023-01-29 08:50  丶拾拾拾拾贰  阅读(9)  评论(0编辑  收藏  举报