02_Java语音进阶||day13_Stream流、方法引用(终章)

第一章 Stream流

1.1 使用传统的方式,遍历集合,对集合中的数据进行过滤

  1. 用来解决已有集合类【重点】库既有的弊端
  2. 例:使用传统的方式,遍历集合,对集合中的数据进行过滤
    1. 只要以张开头的元素
    2. 只要姓名长度为3的人
    public class Demo01List {
        public static void main(String[] args) {
            //创建一个List集合,存储姓名
            List<String> list = new ArrayList<>();
            list.add("张无忌");
            list.add("周芷若");
            list.add("赵敏");
            list.add("张强");
            list.add("张三丰");
    
            //对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合中
            List<String> listA = new ArrayList<>();
            for(String s : list){
                if(s.startsWith("张")){
                    listA.add(s);
                }
            }
    
            //对listA集合进行过滤,只要姓名长度为3的人,存储到一个新集合中
            List<String> listB = new ArrayList<>();
            for(String s : listA){
                if(s.length() == 3){
                    listB.add(s);
                }
            }
    
            //遍历listB集合
            for (String s : listB) {
                System.out.println(s);
            }
        }
    }
    
    //结果:
    张无忌
    张三丰

1.2 使用Stream流的方式,遍历集合,对集合中的数据进行过滤

  1. Stream的优化写法
    • 对listA集合进行过滤,只要姓名长度为3的人,存储到一个新集合中
    public class Demo02Stream {
        public static void main(String[] args) {
            //创建一个List集合,存储姓名
            List<String> list = new ArrayList<>();
            list.add("张无忌");
            list.add("周芷若");
            list.add("赵敏");
            list.add("张强");
            list.add("张三丰");
    
            //对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合中
            //对listA集合进行过滤,只要姓名长度为3的人,存储到一个新集合中
            //遍历listB集合
            list.stream()
                    .filter(name -> name.startsWith("张"))
                    .filter(name -> name.length() == 3)
                    .forEach(name -> System.out.println(name));
        }
    }
    
    //结果:
    张无忌
    张三丰
  1. Stream流是JDK1.8之后出现的
    • 关注的是做什么,而不是怎么做

1.3 流式思想概述




  • 注:
    1. Java中的Stream并不会存储元素,而是按需计算
    2. 数据源,可以是集合,数组等
    3. 使用流的基本步骤
      1. 获取数据源
      2. 数据转换
      3. 执行操作获取想要的结果
    4. 每次转换原有Stream对象不改变返回一个新的Stream对象(可以有多次转换)
    5. 在用filter,map,skip时候集合元素并没有真正被处理,只有当终结方法count执行时候整个模型才会按照指定策略执行。–>得益于Lambda的延迟执行特性

1.4 两种获取Stream流的方式

  1. java.util.stream.Stream
    • 是Java 8新加入的最常用的流接口。(这并不是一个函数式接口。)
  2. 获取一个流非常简单,有以下几种常用的方式:
    • 所有的Collection集合都可以通过stream默认方法获取流
      • default Stream stream()
    • Stream接口的静态方法of可以获取数组对应的流。
      • static Stream of(T… values)
      • 参数是一个可变参数(…),那么我们就可以传递一个数组
    public static void main(String[] args) {
        //把(单列集合)集合转换为Stream流
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        Set<String> set = new HashSet<>();
        Stream<String> stream2 = set.stream();

        Map<String, String> map = new HashMap<>();
        //获取键,存储到Set集合中
        Set<String> keySet = map.keySet();
        Stream<String> stream3 = keySet.stream();
        //获取值,存储到Collection集合中
        Collection<String> values = map.values();
        Stream<String> stream4 = values.stream();
        //获取键值对(键与值的映射关系 entrySet->第二种遍历方式)
        Set<Map.Entry<String, String>> entries = map.entrySet();
        Stream<Map.Entry<String, String>> stream5 = entries.stream();

        //==============================
        //把数组转换为Stream流
        Stream<Integer> stream6 = Stream.of(1, 2, 3, 4, 5, 6);
        //可变参数可以传递数组
        Integer[] arr = {1, 2, 3, 4, 5};
        Stream<Integer> stream7 = Stream.of(arr);
        String[] arr2 = {"a", "b", "c"};
        Stream<String> stream8 = Stream.of(arr2);
    }

1.5 Stream流中的常用方法_逐一处理:forEach

  • 注:除了终结方法,其余方法都是延迟方法
  • 注:终结方法包括count和forEach方法
  1. Stream流中的常用方法_forEach——传递的Consumer接口
    • void forEach(Consumer<? super T> action);
      • 该方法接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理。
      • Consumer接口是一个消费型的函数式接口,可以传递Lambda表达式,消费数据
  2. 简单记:
    • forEach方法,用来遍历流中的数据
    • 是一个终结方法,遍历之后就不能继续调用Stream流中的其他方法
    public static void main(String[] args) {
        //获取一个Stream流
        Stream<String> stream = Stream.of("张三", "李四", "王五", "赵六");
        //使用Stream流中的方法forEach,对Stream流中的数据进行遍历
        /*stream.forEach((String name) -> {
            System.out.println(name);
        });*/

        //优化Lambda
        stream.forEach(name -> System.out.println(name));
    }
    
    //结果:
    张三
    李四
    王五
    赵六

1.6 Stream流中的常用方法_过滤:filter

  1. Stream流中的常用方法_filter:用于对Stream流中的数据进行过滤——传递的Predicate接口
    • Stream filter(Predicate<? super T> predicate);
    • filter方法的参数Predicate是一个函数式接口,所以可以传递Lambda表达式,对数据进行过滤
    • Predicate中的抽象方法:
      • boolean test(T t);//所以有参数,有返回值
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stream = Stream.of("张三丰", "张翠山", "赵敏", "周芷若", "张无忌");
        //对Stream流中的元素进行过滤,只要姓张的人
        Stream<String> stream2 = stream.filter((String name) -> {
            return name.startsWith("张");
        });
        //遍历stream2流
        stream2.forEach(name -> System.out.println(name));
    }
    
    //结果:
    张三丰
    张翠山
    张无忌

1.7 Stream流的特点_只能使用一次

  1. Stream流属于管道流,只能被消费(使用)一次
  2. 1.6中再次调用stream流时
    • 第一个Stream流调用完毕方法,数据就会流转到下一个Stream上
      而这时第一个Stream流已经使用完毕,就会关闭了
    • 所以第一个Stream流就不能再调用方法了
    • 报错信息:IllegalStateException: stream has already been operated upon or closed
        public static void main(String[] args) {
            //创建一个Stream流
            Stream<String> stream = Stream.of("张三丰", "张翠山", "赵敏", "周芷若", "张无忌");
            //对Stream流中的元素进行过滤,只要姓张的人
            Stream<String> stream2 = stream.filter((String name) -> {
                return name.startsWith("张");
            });
            //遍历stream2流
            stream2.forEach(name -> System.out.println(name));
            //再次调用stream流
            stream.forEach(name -> System.out.println(name));
        }
        //结果:
        张三丰
    张翠山
    张无忌
    Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
    ...
    ...
    

1.8 Stream流中的常用方法_映射:map

  1. Stream流中的常用方法_map:用于将一种类型转换为另一种数据类型【重点】——传递的Function接口
    • 如果需要将流中的元素映射到另一个流中,可以使用map方法.
      Stream map(Function<? super T, ? extends R> mapper);
    • 该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。
    • Function中的抽象方法:
      • R apply(T t); //有参数,有返回值
    public static void main(String[] args) {
        //获取一个String类型的Stream流
        Stream<String> stream = Stream.of("1", "2", "3", "4");
        //使用map方法,把字符串类型的整数,转换(映射)为Integer类型的整数
        Stream<Integer> stream1 = stream.map((String s) -> {
            return Integer.parseInt(s);
        });
        //遍历stream1流
        stream1.forEach(i -> System.out.println(i));
    }
    
    //结果:
    1
    2
    3
    4

1.9 Stream流中的常用方法_统计个数:count

  1. Stream流中的常用方法_count:用于统计Stream流中元素的个数
    • long count();
    • count方法是一个终结方法,返回值是一个long类型的整数
      • 所以不能再继续调用Stream流中的其他方法了
  2. 注:和旧集合Collection当中的size方法一样
    public static void main(String[] args) {
        //获取一个Stream流
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        list.add(7);
        //Collection集合,通过stream默认方法获取流
        Stream<Integer> stream = list.stream();
        long count = stream.count();
        System.out.println(count);
    }
    
    //结果:
    7

1.10 Stream流中的常用方法_取用前几个:limit

  1. Stream流中的常用方法_limit:用于截取流中的元素
    • limit方法可以对流进行截取,只取用前n个。方法签名:
    • Stream limit(long maxSize);
      • 参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作
  2. limit方法是一个延迟方法,只是对流中的元素进行截取,返回的是一个新的流,所以可以继续调用Stream流中的其他方法
    public static void main(String[] args) {
        //获取一个Stream流
        String[] arr = {"美羊羊","喜洋洋","懒洋洋","灰太狼","红太狼"};
        Stream<String> stream = Stream.of(arr);
        //使用limit对Stream流中的元素进行截取,只要前3个元素
        Stream<String> stream2 = stream.limit(3);
        //遍历stream2流
        stream2.forEach(s -> System.out.println(s));
    }
    
    //结果:
    美羊羊
    喜洋洋
    懒洋洋

1.11 Stream流中的常用方法_跳过前几个skip

  1. Stream流中的常用方法_skip:用于跳过元素
    • 如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流:
    • Stream skip(long n);
      • 如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。
    public static void main(String[] args) {
        //获取一个Stream流
        String[] arr = {"美羊羊","喜洋洋","懒洋洋","灰太狼","红太狼"};
        Stream<String> stream = Stream.of(arr);
        //使用skip方法跳过前3个元素
        Stream<String> stream2 = stream.skip(3);
        //遍历stream2流
        stream2.forEach(s -> System.out.println(s));
    }
    
    //结果:
    灰太狼
    红太狼

1.12 Stream流中的常用方法_组合concat

  1. Stream流中的常用方法_concat:用于把流组合到一起
    • 如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concat
    • static Stream concat(Stream<? extends T> a, Stream<? extends T> b)
  2. 注:这是一个静态方法,与java.lang.String中的concat方法是不同的
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stream1 = Stream.of("张三丰", "张翠山", "赵敏", "周芷若", "张无忌");
        //获取一个Stream流
        String[] arr = {"美羊羊","喜洋洋","懒洋洋","灰太狼","红太狼"};
        Stream<String> stream2 = Stream.of(arr);
        //把以上两个流组合为一个流
        Stream<String> concat = Stream.concat(stream1, stream2);
        //遍历concat流
        concat.forEach(name -> System.out.println(name));
    }
    
    //结果:
    张三丰
    张翠山
    赵敏
    周芷若
    张无忌
    美羊羊
    喜洋洋
    懒洋洋
    灰太狼
    红太狼

1.13 练习:集合元素处理(传统方式)

  1. 练习:集合元素处理(传统方式)
    • 现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:
    1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
    2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
    3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
    4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
    5. 将两个队伍合并为一个队伍;存储到一个新集合中。
    6. 根据姓名创建Person对象;存储到一个新集合中。
    7. 打印整个队伍的Person对象信息。
    //定义一个Person类:Person.java
    public class Person {
        private String name;
    
        public Person() {
        }
    
        public Person(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    '}';
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }

    //主方法:Demo01StreamTest.java
    public static void main(String[] args) {
        //第一支队伍
        ArrayList<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("石破天");
        one.add("石中玉");
        one.add("老子");
        one.add("庄子");
        one.add("洪七公");
        //1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
        ArrayList<String> one1 = new ArrayList<>();
        for (String name : one) {
            if(name.length() == 3){
                one1.add(name);
            }
        }
        //2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
        ArrayList<String> one2 = new ArrayList<>();
        for(int i = 0; i < 3; i++){
            one2.add(one1.get(i));  //i = 0, 1, 2
        }

        //=====================
        //第二支队伍
        ArrayList<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("赵丽颖");
        two.add("张三丰");
        two.add("尼古拉斯赵四");
        two.add("张天爱");
        two.add("张二狗");
        //3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
        ArrayList<String> two1 = new ArrayList<>();
        for (String name : two) {
            if(name.startsWith("张")){   //startsWith方法用来判断:以..开头
                two1.add(name);
            }
        }
        //4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
        ArrayList<String> two2 = new ArrayList<>();
        for(int i = 2; i < two1.size(); i++){
            two2.add(two1.get(i));  //i 不包含0,1
        }

        //5. 将两个队伍合并为一个队伍;存储到一个新集合中。
        ArrayList<String> all = new ArrayList<>();
        all.addAll(one2);   //集合的添加方法
        all.addAll(two2);

        //6. 根据姓名创建Person对象;存储到一个新集合中。
        ArrayList<Person> list = new ArrayList<>();
        for (String name : all) {
            list.add(new Person(name)); //根据name创建Person
        }

        //7. 打印整个队伍的Person对象信息。
        /*Iterator<Person> it = list.iterator();
        while(it.hasNext()){
            Person next = it.next();
            System.out.println(next);
        }*/
        for (Person person : list) {
            System.out.println(person);
        }
    }
    
    //结果:
    Person{name='宋远桥'}
    Person{name='苏星河'}
    Person{name='石破天'}
    Person{name='张天爱'}
    Person{name='张二狗'}

1.14 练习:集合元素处理(Stream方式)

    //定义一个Person类:Person.java
    public class Person {
        private String name;
    
        public Person() {
        }
    
        public Person(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    '}';
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
    //主方法:Demo02StreamTest.java
    public static void main(String[] args) {
        //第一支队伍
        ArrayList<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("石破天");
        one.add("石中玉");
        one.add("老子");
        one.add("庄子");
        one.add("洪七公");
        //1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。filter
        //2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。limit
        Stream<String> oneStream = one.stream().filter(name -> name.length() == 3).limit(3);
        
        //第二支队伍
        ArrayList<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("赵丽颖");
        two.add("张三丰");
        two.add("尼古拉斯赵四");
        two.add("张天爱");
        two.add("张二狗");
        //3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。filter
        //4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。skip
        Stream<String> twoStream = two.stream().filter(name -> name.startsWith("张")).skip(2);
        
        //5. 将两个队伍合并为一个队伍;存储到一个新集合中。concat
        //6. 根据姓名创建Person对象;存储到一个新集合中。map
        //7. 打印整个队伍的Person对象信息。forEach
        Stream.concat(oneStream, twoStream).map(name -> new Person(name)).forEach(name -> System.out.println(name));
    }
    
    //结果:
    Person{name='宋远桥'}
    Person{name='苏星河'}
    Person{name='石破天'}
    Person{name='张天爱'}
    Person{name='张二狗'}

第二章 方法引用

2.1 方法引用基本介绍

  1. 方法引用–>主要对Lambda表达式的优化
    //定义一个函数式接口:Printable.java
        @FunctionalInterface
        public interface Printable {
            //定义字符串的抽象方法
            void print(String s);
        }
        
        //主方法:Demo01Printable.java
        public class Demo01Printable {
        //定义一个方法,参数传递Printable接口,对字符串进行打印
        public static void printString(Printable p){
            p.print("HelloWorld");
        }
    
        public static void main(String[] args) {
            //调用printString方法,方法的参数Printable是一个函数式接口,所以可以传递Lambda
            printString((s) -> {
                System.out.println(s);
            });
    
            /*
                分析:
                    Lambda表达式的目的,打印参数传递的字符串
                    把参数s,传递给了System.out对象,调用out对象中的方法println对字符串进行了输出
                    注意:
                        1.System.out对象是已经存在的
                        2.println方法也是已经存在的
                    所以我们可以使用方法引用来优化Lambda表达式
                    可以使用System.out方法直接引用(调用)println方法
             */
            printString(System.out::println);
        }
    
    }
    
    //结果:
    HelloWorld
    HelloWorld
  1. 分析:
    • Lambda表达式的目的,打印参数传递的字符串
    • 把参数s,传递给了System.out对象,调用out对象中的方法println对字符串进行了输出
    1. 注意:
      1. System.out对象是已经存在的
      2. println方法也是已经存在的
    • 所以我们可以使用方法引用来优化Lambda表达式
    • 可以使用System.out方法直接引用(调用)println方法

2.2 方法引用符

  1. 双冒号::为引用运算符,而所在表达式称为方法引用
  2. 下面两种等效写法:
    • Lambda表达式:s -> System.out.printn(s);
    • 方法引用写法:System.out::println;
  • 注:Lambda中传递的参数一定是方法引用中的那个方法可以接收的类型,否则会抛出异常

2.3 方法引用_通过对象名引用成员方法

  1. 通过对象名引用成员方法
    • 使用前提是对象名是已经存在的,成员方法也是已经存在
    • 就可以使用对象名来引用成员方法
    //定义一个类:MethodRerObject.java
    public class MethodRerObject {
        //定义一个成员方法,传递字符串,把字符串按照大写输出
        public void printUpperCaseString(String str){
            System.out.println(str.toUpperCase());
        }
    }
    
    //定义一个函数式接口
    @FunctionalInterface
    public interface Printable {
        //定义字符串的抽象方法
        void print(String s);
    }
    
    //主方法:Demo01ObjectMethodReference.java
    public class Demo01ObjectMethodReference {
        //定义一个方法,方法的参数传递Printable接口
        public static void printString(Printable p){
            p.print("Hello");
        }
    
        public static void main(String[] args) {
            //调用printString方法,方法的参数Printable是一个函数式接口,所以可以传递Lambda表达式
            printString((String s) -> {
                //创建MethodRerObject对象
                MethodRerObject obj = new MethodRerObject();
                //调用MethodRerObject对象中的成员方法printUpperCaseString,把字符串按照大写输出
                obj.printUpperCaseString(s);
            });
    
            /*
                使用方法引用优化Lambda
                对象是已经存在的MethodRerObject
                成员方法也是已经存在的printUpperCaseString
                所以我们可以使用对象名引用成员方法
             */
            //创建MethodRerObject对象
            MethodRerObject obj = new MethodRerObject();
            printString(obj::printUpperCaseString);
        }
    }
    
    //结果:
    HELLO
    HELLO

2.4 方法引用_通过类名引用静态成员方法

  1. 通过类名引用静态成员方法
    • 类已经存在,静态成员方法也已经存在
    • 就可以通过类名直接引用静态成员方法
    //定义一函数式接口:Calcable.java
    @FunctionalInterface
    public interface Calcable {
        //定义一个抽象方法,传递一个整数,对整数进行绝对值计算并返回
        int calsAbs(int number);
    }
    
    //主方法:Demo01StaticMethodReference.java
    public class Demo01StaticMethodReference {
        //定义一个方法,方法的参数传递要计算绝对值的整数,和函数式接口Calcable
        public static int method(int number, Calcable c){
            return c.calsAbs(number);
        }
    
        public static void main(String[] args) {
            //调用method方法,传递计算绝对值得整数,和Lambda表达式
            int number = method(-10, (n) -> {
                //对参数进行绝对值得计算并返回结果
                return Math.abs(n);
            });
            System.out.println(number);
    
            /*
                使用方法引用优化Lambda表达式
                Math类是存在的
                abs计算绝对值的静态方法也是已经存在的
                所以我们可以直接通过类名引用静态方法
             */
            int number2 = method(-12, Math::abs);
            System.out.println(number2);
        }
    }
    
    //结果:
    10
    12

2.5 方法引用_通过super引用父类的成员方法

  1. 如果存在继承关系,当Lambda中需要出现super调用时,也可以使用方法引用进行替换
    //定义一个函数式接口:Greetable.java
    @FunctionalInterface
    public interface Greetable {
        //定义一个见面的方法
        void greet();
    }
    
    //定义一个父类:Human.java
    public class Human {
        //定义一个sayHello的方法
        public void sysHello(){
            System.out.println("Hello 我是Human!");
        }
    }
    
    //主方法:Man.java
    public class Man extends Human{
        //子类重写父类sayHello的方法
        @Override
        public void sysHello() {
            System.out.println("Hello 我是Man!");
        }
    
        //定义一个方法参数传递Greetable接口
        public void method(Greetable g){
            g.greet();
        }
    
        //定义一个普通方法
        public  void show(){
            //调用method方法,方法的参数Greetable是一个函数式接口,所以可以传递Lambda
            /*method(() -> {
                //创建父类对象
                Human h = new Human();
                //调用父类的sayHello方法
                h.sysHello();
            });*/
    
            //因为有子父类关系,所以存在的一个关键字super,代表父类,所以我们可以直接使用super调用父类的成员方法
            /*method(() -> {
                super.sysHello();
            });*/
    
            /*
               使用super引用类的成员方法
               super是已经存在的
               父类的成员方法sayHello也是已经存在的
               所以我们可以直接使用super引用父类的成员方法
           */
            method(super::sysHello);
        }
    
        public static void main(String[] args) {
            new Man().show();
        }
    }
    
    //结果:
    Hello 我是Human!

2.6 方法引用_通过this引用本类的成员方法

  1. this代表当前对象,如果需要引用的方法就是当前类中的成员方法,那么可以使用this::成员方法的格式来使用方法引用。
   //定义一个函数式接口:Richable.java
   @FunctionalInterface
    public interface Richable {
        //定义一个想买什么就买什么的方法
        void buy();
    }
    
    //主方法:Husband.java
    public class Husband {
        //定义一个买房子的方法
        public void buyHouse(){
            System.out.println("北京二环内买一套四合院!");
        }
    
        //定义一个结婚的方法,参数传递Richable接口
        public void marry(Richable r){
            r.buy();
        }
    
        //定义一个非常高兴的方法
        public void soHappy(){
            //调用结婚的方法,方法的参数Richable是一个函数式接口,传递Lambda表达式
            /*marry(() -> {
                //使用this.成员方法,调用本类买房子的方法
                this.buyHouse();
            });*/
    
            /*
                使用方法引用优化Lambda表达式
                this是已经存在的
                本类的成员方法buyHouse也是已经存在的
                所以我们可以直接使用this引用本类的成员方法buyHouse
             */
            marry(this::buyHouse);
        }
    
        public static void main(String[] args) {
            new Husband().soHappy();
        }
    }
    
    //结果:
    北京二环内买一套四合院!

2.7 方法引用_类的构造器(构造方法)引用

  1. 由于构造器的名称与类名完全一样,并不固定。所以构造器引用使用类名称::new的格式表示。【重点】
    //定义一个类:Person.java
    public class Person {
        private String name;
    
        public Person() {
        }
    
        public Person(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
    //定义一个函数式接口:PersonBuilder.java
    @FunctionalInterface
    public interface PersonBuilder {
        //定义一个方法,根据传递的姓名,创建Person对象返回
        Person builderPerson(String name);
    }
    
    //主方法:Demo.java
    public class Demo {
        //定义一个方法,参数传递姓名和PersonBuilder接口,方法中通过姓名创建Person对象
        public static void printName(String name, PersonBuilder pb){
            Person person = pb.builderPerson(name);
            System.out.println(person.getName());
        }
    
        public static void main(String[] args) {
            //调用printName方法,方法的参数PersonBuilder接口是一个函数式接口,可以传递Lambda
            /*printName("迪丽热巴", (String name) -> {
                return new Person(name);
            });*/
    
            /*
                使用方法引用优化Lambda表达式
                构造方法new Person(String name) 已知
                创建对象已知 new
                就可以使用Person引用new创建对象
             */
            printName("古力娜扎", Person::new);//使用Person类的带参构造方法,通过传递的姓名创建对象
        }
    }
    
    //结果:
    古力娜扎

2.8 方法引用_数组的构造器引用

  1. 数组也是Object的子类对象,所以同样具有构造器,只是语法稍有不同。
    //定义一个函数式接口:ArrayBuilder.java
    @FunctionalInterface
    public interface ArrayBuilder {
        //定义一个创建int类型数组的方法,参数传递数组的长度,返回创建好的int类型数组
        int[] builderArray(int length);
    }
    
    //主方法:Demo.java
    /*
        数组的构造器引用
     */
    public class Demo {
        /*
            定义一个方法
            方法的参数传递创建数组的长度和ArrayBuilder接口
            方法内部根据传递的长度使用ArrayBuilder中的方法创建数组并返回
         */
        public static int[] createArray(int length, ArrayBuilder ab){
            return ab.builderArray(length);
        }
    
        public static void main(String[] args) {
            //调用createArray方法,传递数组的长度和Lambda表达式
            /*int[] arr1 = createArray(10, (int length) -> {
                //根据数组的长度,创建数组并返回
                return new int[length];
            });
            System.out.println(arr1.length);*/
    
            /*
                使用方法引用优化Lambda表达式
                已知创建的就是int[]数组
                数组的长度也是已知的
                就可以使用方法引用
                int[]引用new,根据参数传递的长度来创建数组
             */
            int[] arr2 = createArray(10, int[]::new);
            System.out.println(Arrays.toString(arr2));
            System.out.println(arr2.length);
        }
    }
    
    //结果:
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    10
发布了42 篇原创文章 · 获赞 6 · 访问量 1126

猜你喜欢

转载自blog.csdn.net/qq_40572023/article/details/104931418
今日推荐