黑马程序员技术交流社区

标题: 就业班day12课堂笔记 [打印本页]

作者: 海贼王和皮卡丘    时间: 2018-12-2 15:31
标题: 就业班day12课堂笔记
day12 函数式接口今日内容    自定义函数式接口
            @FunctionalInterface 注解
    Lambda表达式:
            Lambda表达式的"延迟执行"的特点
            函数式接口作为方法的"参数"和"返回值类型"
    常用函数式接口
            Supplier: 生产型函数式接口         获取值
            Consumer: 消费型函数式接口         使用值
            Predicate: 条件判断型函数式接口    判断值
            Function: 转换型函数式接口         转换值函数式接口函数式接口概念, 自定义函数式接口知识点:    什么是函数式接口
    如何自己定义一个函数式接口
    @FunctionalInterface 注解有什么作用总结:    函数式接口: JDK 8 新特性
        有且仅有一个抽象方法的接口, 适用于函数式编程场景的接口
        (默认方法, 静态方法, 私有方法, 与 java.lang.Object 类中定义相同的抽象方法, 都不算作抽象方法)


    自定义函数式接口:
            接口中有且只有一个抽象方法

    @FunctionalInterface的作用:
            在接口上使用, 检测当前的接口是否为函数式接口补充:    // 格式
    修饰符?interface?接口名称?{
            public?abstract?返回值类型?方法名称(可选参数信息);
            //?其他非抽象方法内容
    }

    // 示例
    @FunctionalInterface
    public interface MyFunctionalInterface {
        void method();
    }函数式接口的使用知识点:    定义了函数式接口后, 如何使用它总结:    函数式接口的使用:
            作为方法的参数类型, 传递Lambda表达式, 代替匿名内部类方式补充:    public static void method(Animal a) {  // 传递实现类对象  Animal a = new Cat();
        a.eat();
    }

    public static void method(Runnable r) {  // 传递实现类对象  Runnable r = new Runnable(){}
        new Thead(r).start();
    }函数式编程性能浪费的日志案例知识点:    演示代码的方法中, 传递的字符串参数是否需要先拼接, 才能传入方法内部总结:    日志: 就是存储程序运行信息的文本文件.
        我们平时打印输出一些信息是在控制台上, 如果将这些信息写入文本文件, 这些文件就是日志

            public static void showLog(int level, String message) {
            if (level == 1) {
                System.out.println(message);
            }
        }

            showLog(2, s1 + s2 + s3);
            // 先拼接s1+s2+s3出结果"helloworldjava", 然后才执行方法内部的判断
            // 相当于方法不需要执行, 但仍然在拼接字符串时浪费了一些性能补充:    使用Lambda延迟执行的特性优化日志案例知识点:    使用Lambda实现相同功能, 能否提高效率总结:    Lambda具有"延迟执行"的特点:
            传递Lambda对象, 只有当符合执行条件时, 才会执行代码
            
            @FunctionalInterface
        interface MessageBuilder {
            String buildMessage();
        }

            public static void showLog(int level, MessageBuilder builder) { // 传递实现类对象
            if (level == 1) {
                    String s = builder.buildMessage();
                System.out.println(s);
            }
        }

        showLog(1, new MessageBuilder(){
                    @Override
            public String buildMessage() {
                return s1 + s2 + s3;
            }
        });

            showLog(2, () -> s1+s2+s3);
            // 传递Lambda对象, 即MessageBuilder实现类对象进方法
            // 判断2和1不相等, 所以不会执行builder.buildMessage()
            // 从而也不会执行重写方法中的s1+s2+s3
            // 避免了无意义的性能浪费补充:    5分钟练习: 对比Lambda和普通方式执行效率    需求:
    定义函数式接口 MessageBuilder, 使用@FunctionalInterface注解修饰
            定义抽象方法用于拼接字符串: String buildMessage();
    定义测试类:
            定义方法模拟拼接大量字符串:
            public static String getString() {
                // 模拟拼接字符串的耗时
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String s1 = "hello";
                String s2 = "world";
                String s3 = "java";
                return s1 + s2 + s3;
            }

            定义方法: public static void showLog(int level, String message)
                    方法内部判断如果level==1, 则打印输出message
            定义方法: public static void showLogLambda(int level, MessageBuilder builder)
                    方法内部判断如果level==1, 则打印输出builder.buildMessage()返回的字符串
            定义main方法:
                    调用 showLog(2, getString()), 在方法调用前后获取毫秒值, 相减得出执行时间, 打印出来
                    调用 showLogLambda(2, ()->getString());在方法调用前后获取毫秒值, 相减得出执行时间, 打印出来代码:    public interface MessageBuilder {
        // 用于拼接字符串的方法, 怎么拼, 需要我们传递Lambda来实现
        String buildMessage();
    }

    public class Test {
        // 模拟拼接大量字符串的方法:
        public static String getString() {
            // 模拟拼接字符串的耗时
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String s1 = "hello";
            String s2 = "world";
            String s3 = "java";
            return s1 + s2 + s3;
        }

        // 普通方式
        public static void showLog(int level, String message) {
            if (level == 1) {
                System.out.println(message);
            }
        }

        // 使用Lambda的延迟执行特性
        public static void showLogLambda(int level, MessageBuilder builder) {
            if (level == 1) {
                String s = builder.buildMessage();
                System.out.println(s);
            }
        }

        public static void main(String[] args) {
            // 调用普通方法
            long start1 = System.currentTimeMillis();
            showLog(2, getString());
            long end1 = System.currentTimeMillis();
            System.out.println("普通方式耗时:" + (end1-start1));

            // 调用Lambda延迟执行的方法
            long start2 = System.currentTimeMillis();
            showLogLambda(2, ()->getString());
            long end2 = System.currentTimeMillis();
            System.out.println("延迟执行方式耗时:" + (end2-start2));
        }
    }使用Lambda表达式作为方法参数知识点:    当一个方法的参数是一个函数式接口时, Lambda表达式方式是否比匿名内部类方式简单
    ... startThread(Runnable r){  // submit(Runnable r)
            new Thread(r).start();
    }总结:    当一个方法的参数是一个函数式接口时, 可以使用Lambda表达式传递该参数, 简化匿名内部类的代码补充:    5分钟练习: 函数式接口作为方法参数    需求:
    定义方法, 用来快速开启线程执行一个任务: public static void startThread(Runnable r)
            方法内部创建一个线程对象, 并将任务传递, 开启线程: new Thread(r).start();
    在main方法中调用startThread()方法, 使用Lambda表达式方式, 传入一个任务, 任务中打印线程名+"线程启动了"代码:    public class Test {
        public static void main(String[] args) {
            // 匿名内部类方式调用方法传递参数
            startThread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "线程启动了");
                }
            });

            // Lambda标准格式传递参数
            startThread(()->{
                System.out.println(Thread.currentThread().getName() + "线程启动了");
            });

            // Lambda省略格式传递参数
            startThread(()->System.out.println(Thread.currentThread().getName() + "线程启动了"));
        }

        // 练习调用参数是函数式接口的方法, 可以向参数传递Lambda表达式
        public static void startThread(Runnable r) {
            new Thread(r).start();
        }
    }
    使用Lambda表达式作为方法返回值知识点:    接口: 可以作为方法的参数类型, 还可以作为方法的返回值类型


    当一个方法的返回值是一个函数式接口时, Lambda表达式方式是否比匿名内部类方式简单
    public static Comparator<String> getComparator(){
        return new Comparator<String>(){
                    @Override
            public int compare(String o1, String o2) {
                    // 编写比较的规则 升序
                    return o1.length()-o2.length();
            }
        };
    }总结:    当一个方法的返回值是一个函数式接口时, 可以返回Lambda表达式, 简化匿名内部类的代码补充:    5分钟练习: 使用Lambda表达式作为返回值    需求:
    定义方法, 用于获取一个比较器对象: public static Comparator<String> getComparator()
        方法内部使用Lambda表达式作为Comparator接口的返回值, 比较规则是:
                    第二个字符串参数的长度 - 第一个字符串参数的长度
    在main方法中
            定义字符串数组: {"aaa", "b", "cccccc", "ddddddddddd"}
            调用 getComparator() 方法获取比较器对象
            调用 Arrays.sort(数组, 比较器) 方法, 将字符串数组排序, 然后打印出来代码:    public class Test {
        // 获取字符串比较器的方法
        public static Comparator<String> getComparator() {
            // 使用匿名内部类方式
            /*return new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return o2.length() - o1.length(); // 降序
                }
            };*/

            // Lambda标准格式
            /*return (String o1, String o2)->{
                return o2.length() - o1.length(); // 降序
            };*/

            // Lambda省略格式
            return (o1, o2)->o2.length() - o1.length();  // 按照字符串长度降序
        }

        public static void main(String[] args) {
            String[] arr = {"aaa", "b", "cccccc", "ddddddddddd"};

            System.out.println("排序前:" + Arrays.toString(arr));
            Arrays.sort(arr, getComparator());
            System.out.println("排序后:" + Arrays.toString(arr));
        }
    }常用函数式接口    Java中提供的常用 函数式接口:
            Supplier<T>: 生产型函数式接口         获取值  a和b的和, 获取数组最大值, 获取一个Person对象
            Consumer<T>: 消费型函数式接口         使用值  给它一个String让他打印, 给它一个对象
            Predicate<T>: 条件判断型函数式接口     判断值  判断字符串长度是否大于5
            Function<T, R>: 转换型函数式接口      转换值  给它一个String转换为Integer "123"->123Supplier生产型函数式接口知识点:    JDK 提供的函数式接口在哪个包中
    Supplier<T> 接口有什么作用总结:    java.util.function.Supplier<T>函数式接口: 生产型函数式接口
            // 抽象方法
             T?get(): 用于获取一个对象或值.
                至于获取什么值, 怎么获取, 需要我们根据应用场景编写Lambda来实现补充:    public class Test {
        public static void main(String[] args) {
            // 调用方法
            String s = getString( ()->"赵丽颖" );
            System.out.println(s);  // 赵丽颖

            // 相当于
            getString(new Supplier<String>(){
                @Override
                public String get() {
                    return "赵丽颖";
                }
            });
        }

        // 定义方法: 用来获取一个字符串 参数: 获取字符串的方式(代码)
        public static String getString(Supplier<String> sup) {  // 接口实现类对象
            return sup.get();
        }
    }Supplier生产型函数式接口练习: 求数组最大值5分钟练习: 使用Supplier接口求数组最大值    需求:
    使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值
    定义测试类:
            定义方法, 用于获取数组中的最大值: public static int getMax(Supplier<Integer> sup)
                    方法内部直接调用sup对象的 get() 方法获取得到的结果, 并return
            定义main方法:
                    方法中定义数组: {100, 0, -50, 88, 99, 33, -30}
                    调用 getMax() 方法, 方法内部传递Supplier接口的Lambda表达式
                    表达式中编写获取数组最大值的代码代码:    public class Test {

        // 方法的作用: 获取最大值  至于怎么获取, 获取什么的最大值, 需要我们传递Lambda表达式来实现
        // 参数的作用: 获取最大值的方式(代码)
        public static int getMax(Supplier<Integer> sup) {
            return sup.get();
        }

        public static void main(String[] args) {
            int[] arr =  {100, 0, -50, 88, 99, 33, -30};

            // 调用方法来获取数组中的最大值, 怎么获取呢? 我们来传递Lambda表达式实现

            // 一开始不熟悉抽象方法的参数和返回值类型, 可以先写匿名内部类, 然后改写为Lambda
            /*int maxValue = getMax(new Supplier<Integer>() {
                @Override
                public Integer get() {
                    int max = 0;
                    for (int i : arr) {
                        if (i > max) {
                            max = i;
                        }
                    }
                    return max;
                }
            });*/

            // Lambda方式
            int maxValue = getMax(()->{
                int max = 0;
                for (int i : arr) {
                    if (i > max) {
                        max = i;
                    }
                }
                return max;
            });

            System.out.println("最大值:" + maxValue);
        }
    }Consumer消费型函数式接口知识点:    Consumer<T> 接口有什么作用总结:    java.lang.StringBuilder类: 可变字符序列, 类似于String, 线程不安全效率高   
            StringBuilder reverse(): 将StringBuilder内部保存的内容反转
            String toString(): 转换为String
                    new StringBuilder("abc").reverse().toString(); -> "cba" "abc" -> "cba"
    java.lang.StringBuffer类: 可变字符序列, 类似于String, 线程安全效率低
            StringBuffer reverse(): 将StringBuffer内部保存的内容反转
            String toString(): 转换为String


    java.util.function.Consumer<T>函数式接口: 消费型函数式接口
            // 抽象方法
            void?accept(T?t): 用于消费(使用)一个对象或值. 至于怎么消费, 要我们根据应用场景编写Lambda实现
            // 默认方法
            default?Consumer<T>?andThen(Consumer<??super?T>?after): 拼接两个Consumer接口的Lambda对象实现连续操作. 谁写前面, 谁先消费补充:    public class Test {

            // 定义方法用来消费一个字符串. 参数是 被消费的字符串 和 消费方式
        public static void method(String name, Consumer<String> con){
            con.accept(name);
        }

        public static void main(String[] args) {
            String name = "赵丽颖";

                    // 消费这个字符串, 我们的消费方式是 反转字符串
            method(name, (String name)->{
                    // 反转字符串
                String reName = new StringBuilder(name).reverse().toString();
                System.out.println(reName);  // 颖丽赵

            });
        }
    }Consumer消费型函数式接口: 默认方法andThen()知识点:    Consumer<T>接口中的默认方法 Consumer<T> andThen(Consumer<??super?T>?after) 如何简化多个Consumer的消费总结:    java.util.function.Consumer<T>函数式接口: 消费型函数式接口
            // 默认方法
            default?Consumer<T>?andThen(Consumer<??super?T>?after): 拼接两个Consumer接口的Lambda对象实现连续操作. 谁写前面, 谁先消费补充:    public class Test {

            // 定义方法用来消费一个字符串. 参数是 被消费的字符串 和 消费方式1 和 消费方式2
        public static void method(String s, Consumer<String> con1, Consumer<String> con2){
    //        con1.accept(s);
    //        con2.accept(s);

                    // 使用andThen简化
            con1.andThen(con2).accept(s);
        }

        public static void main(String[] args) {
                    // 传入两种消费方式
            method("Hello",
                   (t) -> {
                       System.out.println(t.toUpperCase()); // HELLO
                   },
                   (t) -> {
                       System.out.println(t.toLowerCase()); // hello
                   });
        }
    }Consumer消费型接口练习: 字符串拼接输出5分钟练习: Consumer接口实现字符串拼接输出    需求:
    字符串数组String[] array = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男"};当中存有多条信息, 请按照格式"姓名:XX。性别:XX。"格式将信息打印出来.
    要求:
            将打印姓名的动作, 作为第一个Consumer接口的Lambda实例
            将打印性别的动作, 作为第二个Consumer接口的Lambda实例
            将两个Consumer接口按照顺序"拼接"到一起

    public class Test {
        public static void printInfo(String[] arr, Consumer<String> con1, Consumer<String> con2) {
                    // 补全代码
        }

        public static void main(String[] args) {
            String[] array = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男"};

            // 调用printInfo方法
        }
    }


    // 最终效果
    姓名:迪丽热巴。性别:女。
    姓名:古力娜扎。性别:女。
    姓名:马尔扎哈。性别:男。


    分析:
    被消费的字符串s: "迪丽热巴,女"
    // 消费方式1
    String[] split = s.split(",");
    String name = split[0];
    System.out.print("姓名:"+name)

    // 消费方式2
    String[] split = s.split(",");
    String gender = split[1];
    System.out.println("。性别:"+gender+"。")








欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2