• jdk8新特性(Lambda、Steam、函数式接口)


    JDK8新特性

    Java 8 (又称为jdk 1.8)是Java语言开发的一一个主要版本。
    Java 8是oracle公司于2014年3月发布,可以看成是自Java5以来最具革命性的版本。Java 8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。

    特点

    • 速度更快

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

    • 强大的Stream API

    • 便于并行最大化减少空指针异常: Optional

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

    Lambda表达式

    Lambda初体验

    -> : Lambda 操作符、箭头操作符

    左边:Lambda形参列表(就是抽象方法里的形参)

    右边:Lambda 方法体(就是重写抽象方法的方法体)

       @Test
        public void test1() {
            // 普通写法
            Runnable r1 = new Runnable() {
                @Override
                public void run() {
                    System.out.println("普通写法");
                }
            };
            r1.run();
    
            System.out.println("*********************************");
            // Lambda方式
            Runnable r2 = () -> System.out.println("Lambda写法");
            r2.run();
        }
    
        // 普通方式
        @Test
        public void test2() {
    
        }
    
        @Test
        public void test3() {
            Comparator<Integer> c1 = new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return Integer.compare(o1, o2);
                }
            };
            System.out.println(c1.compare(10, 21));
    
            System.out.println("*********************");
            Comparator<Integer> c2 = (o1, o2) -> {
                return Integer.compare(o1, o2);
            };
            System.out.println(c2.compare(21, 2));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    Lambda 表达式的使用

    1. 形参可以省略参数类型,编译器自动进行 “类型推断”
    2. Lambda 若只需要一个参数时,参数的小括号可以省略
        @Test
        public void test4() {
            Consumer<String> c1 = s -> {
                System.out.println(s);
            };
            c1.accept("省略参数小括号");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. Lambda 方法体有多条语句时,允许有 return 返回值

    2. Lambda 方法体只有一条语句时,return 和 大括号 都可以省略

        @Test
        public void test5() {
            Comparator<Integer> c2 = (o1, o2) -> Integer.compare(o1, o2);
            System.out.println(c2.compare(21, 2));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Lambda表达式的本质: 就是接函数式口的实例,重写接口中的方法

    函数式(Functional)接口

    如果一个接口中,只声明了一个抽象方法,该接口就是函数式接口

    • 只包含一个抽象方法的接口,称为函数式接口。
    • 你可以通过Lambda表达式来创建该接口的对象。 (若Lambda表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)
    • 我们可以在一一个接口.上使用==@FunctionalInterface==注解,这样做可以检查它是否是-一个函数式接口。同时javadoc也会包含一条声明, 说明这个接口是一个函数式接口。
    • 在java.util.function包下定 义了Java 8的丰富的函数式接口

    Java提供的四大函数式接口:

    image-20221103084722839

    Consumer接口:

        @Test
        public void test() {
            // 普通写法
            method1(200d, new Consumer<Double>() {
                @Override
                public void accept(Double aDouble) {
                    System.out.println("消费了 " + aDouble);
                }
            });
            System.out.println("********************************");
            // Lambda 写法
            method1(300d, money -> System.out.println("消费了 " + money));
    
        }
    
        public void method1(Double money, Consumer<Double> consumer) {
            consumer.accept(money);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Predicate接口:

        @Test
        public void test2() {
            List<String> lists = new ArrayList<>();
            lists.add("普京");
            lists.add("北京");
            lists.add("西京");
            lists.add("南京");
           // 普通写法
           List<String> list = method2(lists, new Predicate<String>() {
                @Override
                public boolean test(String s) {
                    return s.contains("京");
                }
            });
            System.out.println(list);
            System.out.println("*************************************");
            // Lambda 写法
            List<String> list1 = method2(lists,s -> s.contains("京"));
            System.out.println(list1);
        }
    
        public List<String> method2(List<String> list, Predicate<String> pre) {
            ArrayList<String> newList = new ArrayList<>();
            for (String s : list) {
                if (pre.test(s)) {
                    newList.add(s);
                }
            }
            return newList;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    其他接口:

    image-20221103090106993

    方法引用与构造器引用

    方法引用

    • 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用 !
    • 方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一一个语法糖。

    要求 : 实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致 !(针对情况一和情况二)

    格式: 使用操作符 “::” 将类(或对象)与方法名分隔开来。

    • 如下三种主要使用情况:

      ➢对象::实例方法名

      ➢类::静态方法名

      ➢类::实例方法名 (特殊)

    个人理解

    ​ Lambda 和 方法引用都可以看做是函数式接口的实例,主要目的都是为了重写接口中的抽象方法。

    ​ Lambda 重写抽象方法主要是自己编写方法体

    ​ 方法引用重写抽象方法主要是 使用 方法的引用 编写方法体。但是具体是什么样的方法是由要求的。

    代码演示:

       // 情况一:对象::实例方法
        @Test
        public void test1() {
            // Lambda 方式
            Consumer<String> con = s -> System.out.println(s);
            con.accept("Lambda方式");
    
            // 方法引用
            // void accept(T t);
            // void println(T t)
            // 接口中的抽象方法的形参列表和返回值,与方法引用的方法形参列表、返回值都一样。
            Consumer<String> con1 = System.out::println;
            con1.accept("方法引用");
        }
    
        // 情况二:类::静态方法
        @Test
        public void test3() {
            // Lambda方式
            // int compare(T o1, T o2);
            //  static int compare(int x, int y)
            Comparator<Integer> com1 = (o1,o2) -> Integer.compare(o1,o2);
            System.out.println(com1.compare(21, 22));
            // 方法引用的方式
            Comparator<Integer> com2 = Integer::compare;
            System.out.println(com1.compare(23, 22));
        }
    
        // 情况三: 类::实例方法(比较特殊)
        @Test
        public void test4() {
            // Lambda 方式
            // int compare(T o1, T o2);
            // int compareTo(String anotherString)
            // 这种情况比较特殊,接口中抽象方法的形参列表和方法引用中的形参列表不一样
            // 但是该方法是通过另外一个参数调用的,因此也可以使用方法引用
            Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
            // 方法引用方式
            Comparator<String> com2 = String::compareTo;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    情况三比较特殊:当方法引用中的形参列表中的一个参数作为方法的调用者时,也可以使用方法引用

    构造器引用

      // 传一个参数
        @Test
        public void test1() {
            // Lambda 方式
            Function<Long,User> fun1 = id -> new User(id);
            System.out.println(fun1.apply(10001L));
    
            // 构造器引用
            Function<Long,User> fun2 = User::new;
            System.out.println(fun2.apply(10002L));
    
        }
    
        // 传俩个参数
        @Test
        public void test2() {
            // Lambda 方式
            BiFunction<Long,String,User> bifun1 = (id,name) -> new User(id,name);
            System.out.println(bifun1.apply(10L, "李四"));
            // 构造器引用
            BiFunction<Long,String,User> bifun2 = User::new ;
            System.out.println(bifun2.apply(20L, "王五"));
        }
    
        // 数组引用: 将数组看成一个类,和构造器引用一样
        @Test
        public void test3() {
            // Lambda 方式
            Function<Integer,String[]> fun1 = length -> new String[length];
            System.out.println(Arrays.toString(fun1.apply(5)));
            // 数组引用
            Function<Integer,String[]> fun2 = String[]::new;
            System.out.println(Arrays.toString(fun2.apply(10)));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    强大的 StreamAPI

    Java8中有两大最为重要的改变。第一一个是Lambda表达式;另外- 一个则是Stream API。|

    • Stream API ( java.util.stream)把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充
    • Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用Stream API来并行执行操作。

    简言之,StreamAPl 提供了一种高效且易于使用的处理数据的方式。

    为什么要使用 Stream Api ?

    • 实际开发中,项目中多数数据源都来自于Mysq|l,Oracle等。 但现在数据源可以更多了,有MongDB,Radis等, 而这些NoSQL的数据就需要Java层面去处理。

    • Stream和Collection集合的区别: Collection是一种静态的内存数据结构,而Stream是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU实现计算。

    Stream到底是什么呢?

    是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,Stream讲的是计算!”

    注意

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

    Stream的操作三个步骤

    • 创建Stream
      • 一个数据源(如:集合、数组),获取一个流
    • 中间操作
      • 一个中间操作链,对数据源的数据进行处理
    • 终止操作(终端操作)
      • 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用

    image-20221103120252231

    创建Stream方式

    通过集合

    Java8中的Collection接口被扩展,提供了两个获取流的方法:

    default Stream stream(): 返回一个顺序流
    default Stream parallelStream(): 返回一一个并行流

        @Test
        public void test1() {
            // 方式一:通过集合
            List<User> userList = User.getUserList();
            Stream<User> stream = userList.stream();
    
            // 返回一个并行流
            Stream<User> parallelStream = userList.parallelStream();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    通过数组

    Arrays 中定义了获取 stream 的方法:

    public static  Stream stream(T[] array) {}
    
    • 1
        @Test
        public void test2() {
            // 方式二:通过数组
            User[] userArray = User.getUserArray();
            Stream<User> stream = Arrays.stream(userArray);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    通过Stream的of

        @Test
        public void test3() {
            // 通过Stream的of
            Stream<Integer> integerStream = Stream.of(1, 2, 3, 4);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以使用静态方法Stream.iterate()和Stream.generate(),创建无限流

    public static Stream iterate(final T seed, final UnaryOperator f) 迭代
    public static Stream generate(Supplier s) 生成

        @Test
        public void test4() {
            // 方式四: 通过 iterate 和 generate
            // 获取20以内的偶数
            Stream.iterate(0,t->t+2).limit(10).forEach(System.out::println);
            // 生成数据
            Stream<List<User>> generate = Stream.generate(User::getUserList);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    Stream 的中间操作

    筛选与切片

    image-20221103124713511

    代码演示:

       // 1. 筛选与切片
        @Test
        public void test1() {
            Stream<User> stream = User.getUserList().stream();
            // Stream filter(Predicate predicate); 从流中排除某些元素
            // forEach: 流的终止操作
            // 排除工资低于 2000 的
            stream.filter(user -> user.getSalary() > 2000).forEach(System.out::println);
    
            System.out.println("*****************************************************");
            Stream<User> stream1 = User.getUserList().stream();
            // Stream limit(long maxSize); 截断流,是其元素不得超过指定数
            stream1.limit(2).forEach(System.out::println);
            // 报错:java.lang.IllegalStateException: stream has already been operated upon or closed
            // 流一旦执行终止操作就会关闭,想要在使用再重新创建一个流
            // stream1.limit(3).forEach(System.out::println);
            
            System.out.println("*****************************************************");
            Stream<User> stream2 = User.getUserList().stream();
            // Stream distinct(); 筛选流,通过元素的的hashCode和equals方法去除重复元素
            stream2.distinct().forEach(System.out::println);
    
            System.out.println("*****************************************************");
            Stream<User> stream3 = User.getUserList().stream();
            // Stream skip(long n); 跳过 n 个元素,若不足n个元素,则返回一个空流
            stream3.skip(3).forEach(System.out::println);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    映射

    image-20221103172521778

        // 映射
        @Test
        public void test2() {
            List<User> users = User.getUserList();
            
            //  Stream map(Function mapper);
            Stream<Double> stream = users.stream().map(User::getSalary);
            System.out.println(stream);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    排序

    image-20221103173335997

    代码演示:

        @Test
        public void test1() {
            Stream<Integer> stream = Arrays.asList(2, 1, 33, 22, 11).stream();
            // Stream sorted(); 按自然顺序排序
            stream.sorted().forEach(System.out::println);
    
            Stream<User> stream1 = User.getUserList().stream();
            //    Stream sorted(Comparator comparator); 自定义排序规则
            stream1.sorted(Comparator.comparingDouble(User::getSalary)).forEach(System.out::println);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    Stream 的终止操作

    匹配与查找

    boolean allMatch(Predicate predicate);检查流中所有元素都匹配
    boolean anyMatch(Predicate predicate);检查流中元素是否至少有一个匹配
    boolean noneMatch(Predicate predicate);检查流中元素是否都不匹配
    Optional findFirst();返回流中第一个元素
    Optional findAny();返回流中最后一个元素
    long count();返回流中的个数
    Optional max(Comparator comparator);返回流中最大的的元素
    Optional min(Comparator comparator);返回流中最小的元素
    void forEach(Consumer action);迭代遍历

    代码演示:

     @Test
        public void test1() {
            List<User> users = User.getUserList();
            // boolean allMatch(Predicate predicate); 检查流中是否所有元素都匹配
            // 检查是否所有的用户工资都大于 2000
            boolean allMatch = users.stream().allMatch(user -> user.getSalary() > 2000);
            System.out.println(allMatch);
    
            //  boolean anyMatch(Predicate predicate); 检查流中是否有元素匹配上
            boolean anyMatch = users.stream().anyMatch(user -> user.getSalary() > 10000);
            System.out.println(anyMatch);
    
            // boolean noneMatch(Predicate predicate); 检查流中是否都不匹配
            boolean noneMatch = users.stream().noneMatch(user -> user.getName().startsWith("王"));
            System.out.println(noneMatch);
    
    
            // Optional findFirst(); 返回流中的第一个元素
            Optional<User> first = users.stream().findFirst();
            System.out.println(first);
            // Optional findAny(); 随机返回流中的元素
            Optional<User> any = users.stream().findAny();
            System.out.println(any);
    
            //long count(); 返回流中元素的个数
            long count = users.stream().count();
            System.out.println(count);
    
            //  Optional max(Comparator comparator); 返回流中的最大值
            // 练习:返回工资最高的用户
            Optional<User>   max = users.stream().max(Comparator.comparingDouble(User::getSalary));
            System.out.println(max);
    
            //Optional min(Comparator comparator); 返回流中的最小值
            // 练习:返回用户中最少的工资
            // 使用 map 映射 工资,返回一个只有工资的流
            Optional<Double> min = users.stream().map(User::getSalary).min(Double::compare);
            System.out.println(min);
    
            // void forEach(Consumer action); 迭代遍历
            users.stream().forEach(System.out::println);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    归约

    image-20221103184203723

    代码演示:

        // 归约
        @Test
        public void test2() {
            List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
            // T reduce(T identity, BinaryOperator accumulator); 将流中的值反复结合起来得到一个新的值
            // 练习:计算1-10的和
            Integer reduce = integerList.stream().reduce(0, Integer::sum);
            System.out.println(reduce);
    
            List<User> users = User.getUserList();
            // Optional reduce(BinaryOperator accumulator); 将流中的值反复结合起来得到一个新的值,返回Optional
            // 练习:计算所有用户的工资总和
            Optional<Double> reduce1 = users.stream().map(User::getSalary).reduce(Double::sum);
            System.out.println(reduce1);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    收集

    image-20221103224946666

    形参需要使用 Collectors 提供实例,调用以下方法

    image-20221103224939085

    Optional 类

    • 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类 已经成为Java 8类库的一部分。

    • Optional 类(java.util.Optional) 是-一个 容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用null表示-一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。

    • Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则 isPresent()方法会返回true,调用get()方法会返回该对象。

    image-20221103191137885

  • 相关阅读:
    java毕业设计花店管理系统(附源码、数据库)
    【es6】数值扩展和对象扩展
    小程序开发设计-第一个小程序:创建小程序项目④
    Node.js 入门教程 12 使用 Node.js 输出到命令行
    算法-卡尔曼滤波之卡尔曼滤波的其他三个方程
    【k8s】【网络】网络连通性问题排查
    计算机组成原理(谭志虎主编 )
    【CentOS】忘记root密码
    【JavaWeb】品牌管理系统前后端代码实现
    GitHub工具之云资产管理
  • 原文地址:https://blog.csdn.net/aetawt/article/details/127949647