Java进阶学习7:【线程池、Lambda表达式、函数式接口】

  • A+
所属分类:Java Java进阶

01. 线程池的介绍

参考 Java基础学习15:【线程池、Lambda、函数式接口】中的图

02. 线程池相关API以及线程池的使用

/*
    线程池就是一个容器,里面存放了很多线程,里面的线程可以复用的, 可以多次执行任务。

    相关API:
        Executor(接口):Executor是所有线程池的顶层接口, 里面仅仅定义了执行线程任务的方法。
        ExecutorService(接口):Executor的子接口,也表示线程池。 这个接口中不仅定义了执行线程任务                               的方法,还定义了其他操作线程池的方法。
        Executors(工具类): Executors是操作线程池的工具类, 里面定义了一些方法, 可以直接获取线程                            池对象。

    注意: 线程池对象不能由我们自己new, 而是要通过Executors这个工具类进行获取。

    Executors中获取线程池的方法:
        static ExecutorService newFixedThreadPool(int nThreads): 用来获取一个定长的线程池,          参数为线程池的长度。

    ExecutorService操作线程池的方法:
        submit(Runnable task): 使用线程池执行任务。
        shutdown(): 销毁线程池

    线程池的使用步骤:
        1. 使用Executors的静态方法获取线程池对象。
        2. 定义Runnable接口的实现类,重写run方法,定义要执行的任务。
        3. 调用submit方法,传递Runnable接口的实现类对象, 指定并执行Runnable实现类中的任务。
        4. 销毁线程池(一般不做)
 */
public class Demo01ThreadPool {
    public static void main(String[] args) {
        //使用Executors的静态方法获取线程池对象。
        ExecutorService pool = Executors.newFixedThreadPool(2);//2表示线程池中有两个线程.
        //调用submit方法,传递Runnable接口的实现类对象, 指定并执行Runnable实现类中的任务。
        MyTask myTask = new MyTask();
        pool.submit(myTask);
        pool.submit(myTask);
        pool.submit(myTask);
        //销毁线程池
        //pool.shutdown();
    }
}

public class MyTask implements Runnable{
    @Override
    public void run() {
        //输出100行HelloWorld
        for(int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "正在输出HelloWorld:"             + i);
        }
    }
}

03. 匿名内部类的冗余写法以及Lambda表达式初体验

/*
    匿名内部类的格式:
        new 父类或接口() {
            重写的方法
        }

    匿名内部类格式是十分冗余(多余)的
    因为Thread构造方法位置需要传递一个Runnable类型的参数, 所以我们不得不写了一个new Runnable
    因为在实现类中要重写接口中的抽象方法, 方法重写时根据语法规定, 修饰符 返回值类型 方法名 参数这些内容要和父类一样,所以我们又不得不写了方法的声明部分。
    匿名内部类的写法为了遵循面向对象的语法,而不得不写了很多东西。

    匿名内部类中最核心的东西是方法,而方法中最核心的东西有前中后三点
        前: 参数
        中: 方法体
        后: 返回值

    如果使用lambda表达式,可以让我们关注整个匿名内部类中最核心的这三点
    Lambda表达式是匿名内部类的简化写法

    Lambda表达式是函数式编程思想
    面向对象主要关注的是怎么做
    函数式编程思想主要关注的是做什么事情。
 */
public class Demo01Inner {
    public static void main(String[] args) {
        //使用匿名内部类,实现一个多线程的程序
        new Thread(new Runnable() {
            public void run() {
                System.out.println(Thread.currentThread().getName() + "执行了");
            }
        }).start();

        //lambda表达式初体验
        new Thread(() -> System.out.println(Thread.currentThread().getName() + "执行          了")).start();
    }
}

04. Lambda表达式的标准格式

/*
    匿名内部类的好处是可以省略创建.java文件的操作
    匿名内部类也有缺点, 他的缺点是语法复杂, 写法也非常冗余。

    匿名内部类中最关键的内容是方法参数, 方法体, 返回值。
    所以最好的方式是只关注这核心的三个内容。
    lambda表达式就是只关注着三个内容

    Lambda表达式的标准格式:
        (参数类型 参数名) -> {
            方法体;
            返回值;
        }
    注意:
        1. 小括号里面可以写多个参数,多个参数之间用逗号隔开。
        2. ->是一个运算符,表示指向性动作。
        3. 大括号中要写方法体和返回值, 和之前传统方法的写法基本一致。

    Lambda表达式可以省略面向对象中条条框框,让我们只关注最核心的内容。

    函数式编程思想: 可推导,就是可省略。
    因为Thread构造方法中要传递一个Runnable接口类型的参数,所以能够推导出来参数类型, 所以可以省略new Runnable
    因为实现接口的时候要重写接口中的抽象方法, 重写之后的方法修饰符 返回值类型 方法名要和父类一致,所以我们可以省略要重写的方法的修饰符 返回值类型这些内容。

    Lambda表达式是匿名内部类的简化写法
 */
public class Demo02Inner {
    public static void main(String[] args) {
        //创建Runnable接口的实现类对象
        MyTask myTask = new MyTask();
        new Thread(myTask).start();
        //使用匿名内部类实现
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "执行了");
            }
        }).start();
        //使用Lambda表达式完成多线程
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "执行了");
        }).start();
    }
}
public class MyTask implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "执行了");
    }
}

05. Lambda表达式进行比较器排序

public class Demo03ComparatorTest {
    public static void main(String[] args) {
        //创建集合, 保存学生
        ArrayList<Student> list = new ArrayList<>();
        //添加
        list.add(new Student("大幂幂", 22));
        list.add(new Student("柳岩", 20));
        list.add(new Student("李小璐", 30));
        //使用比较器排序对学生对象根据年龄升序排序
        //Collections.sort(list, new Rule());
        //使用匿名内部类去完成
        /*
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getAge() - o2.getAge();
            }
        });
        */

        //使用Lambda表达式完成排序
        /*
        Collections.sort(list, (Student o1, Student o2) -> {
            return o1.getAge() - o2.getAge();
        });
        */
        Collections.sort(list, (o1, o2) -> o1.getAge() - o2.getAge());

        //输出集合中的内容
        System.out.println(list);
    }
}
public class Rule implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getAge() - o2.getAge();
    }
}

06. Lambda表达式的省略格式

/*
    Lambda标准格式:
        (参数类型 参数名) -> {
            方法体;
            return 返回值;
        }

    省略规则:
        1. 小括号中的参数类型可以省略。
        2. 如果小括号中只有一个参数,那么可以省略小括号。
        3. 如果大括号中只有一条语句, 那么无论这个方法有没有返回值,都可以省略大括号, return,分号
 */
public class Demo04SimpleLambda {
    //定义方法,使用MyInterface接口当做参数
    public static void method(MyInterface m) {
        m.printStr("hello");
    }

    public static void main(String[] args) {
        //调用method方法
        /*
        method(new MyInterface() {
            @Override
            public void printStr(String str) {
                System.out.println("匿名内部类输出:" + str);
            }
        });
        */

        //调用method方法,传递Lambda表达式的标准格式
        method((String str) -> {
            System.out.println("Lambda表达式打印:" + str);
        });
        //小括号中的参数类型可以省略。
        method((str) -> {
            System.out.println("Lambda表达式打印:" + str);
        });

        //如果小括号中只有一个参数,那么可以省略小括号。
        method(str -> {
            System.out.println("Lambda表达式打印:" + str);
        });

        //如果大括号中只有一条语句, 那么无论这个方法有没有返回值,都可以省略大括号, return,分号
        method(str -> System.out.println("Lambda表达式打印:" + str));
    }
}

public interface MyInterface {
    //定义一个抽象方法
    void printStr(String str);
}

07. Lambda表达式的使用前提

/*
    Lambda表达式的前提
        1. 必须有接口(不能是抽象类), 并且接口中必须有且仅有一个需要被重写的抽象方法
        2. 必须支持上下文推导(要么有接口当做参数, 要么使用接口接收Lambda表达式)

    Lambda表达式是匿名内部类的简化写法, 但是他们并不能完全替换
        1. 匿名内部类不仅可以是接口,还可以是类。而Lambda表达式要求只能是接口。
        2. 匿名内部类中可以重写多个方法。但是Lambda表达式只能有一个重写的抽象方法。

 */
public class Demo05LambdaBefore {
    public static void method(MyInterface m) {
        m.printStr("hello");
    }

    public static void main(String[] args) {
        //method(str -> System.out.println("Lambda表达式打印:" + str));

        MyInterface m = str -> System.out.println("Lambda表达式打印:" + str);
        m.printStr("你好");
    }
}

08. 函数式接口的介绍

/*
    函数式接口

    函数式接口指的是【有且仅有一个需要被重写的抽象方法】的接口。
    函数式接口可以当做Lambda表达式的使用前提。

    在Java中,有一个注解可以验证一个接口是否是函数式接口,这个注解叫做@FunctionalInterface
    我们可以在接口名字上使用这个注解验证一个接口是否是函数式接口,如果加上这个注解没有报错
    那么这个接口就是一个函数式接口,如果报错了,那么它就不是函数式接口。

    这个注解仅仅用来验证一个接口是否是函数式接口,如果不使用这个注解,那么接口只要满足规则,那么
    同样也是一个函数式接口。
 */
@FunctionalInterface
public interface MyInterface {
    void method();
}

09. 函数式接口Supplier的get方法

/*
    在JDK8的时候,提供了一个包(java.util.function),这个包里面存放了很多常见的函数式接口。

    其中有一个函数式接口叫做Supplier,这个接口表示生产者, 可以通过这个接口获取数据。

    Supplier<T>中的泛型T: 要使用Supplier获取什么类型的数据,那么这个T就是什么类型

    Supplier中有一个抽象方法叫做get,可以获取一个数据:
        T get(): 获取一个数据
 */
public class Demo01Supplier {
    //定义方法,使用Supplier函数式接口当做方法参数
    public static void method(Supplier<String> supplier) {
        String str = supplier.get();
        System.out.println(str);
    }

    public static void main(String[] args) {
        //调用method方法
        /*
        method(new Supplier<String>() {
            @Override
            public String get() {
                return "你好";
            }
        });
         */
        //调用method方法,传递Lambda表达式
        method(() -> "Hello");
    }
}

10. 使用Supplier的get方法获取数组最大值

/*
    要求:
    使用Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。提示:接口的泛型请使用
    java.lang.Integer 类。
 */
public class Demo02SupplierTest {
    public static void main(String[] args) {
        //定义一个数组
        int[] arr = {5, -10, 2, 6, 4};
        //调用method方法,求出最大值
        method(() -> {
            //定义变量,表示参照物,保存每次比较产生的最大值.
            int max = arr[0];
            //遍历数组,拿到里面的每一个元素和max进行比较
            for(int i = 1; i < arr.length; i++) {
                if(arr[i] > max) {
                    max = arr[i];
                }
            }
            //返回结果
            return max;
        });
    }

    /*
        定义方法,使用Supplier当做方法参数, 该方法求出int数组的最大值
     */
    public static void method(Supplier<Integer> s) {
        //通过get方法获取最大值, 因为get是抽象方法,到底怎么获取,现在并不清楚
        int max = s.get();
        System.out.println("max:" + max);
    }
}

11. Consumer中的accept方法

/*
    在Java中有一个函数式接口叫做Consumer,这个函数式接口可以看成一个消费者,可以消费(使用)一个数据

    抽象方法:
        void accept(T t): 用来消费一个数据(使用这个数据去干一些事情)
 */
public class Demo01Consumer {
    //定义方法,接收一个函数式接口Consumer类型的参数
    public static void method(Consumer<String> c) {
        //调用accept使用hello
        c.accept("hello");
    }

    public static void main(String[] args) {
        //调用method方法,传递lambda表达式
        method(s -> System.out.println(s));
    }
}

12. Consumer中的andThen方法

/*
    在Consumer中还有一个默认方法,这个默认方法可以对两个Consumer进行合并。

    默认方法:
        default Consumer andThen(Consumer after): 对两个Consumer进行合并,返回合并后的结果。

    举例:
        Consumer a: 将Hello转成大写打印
        Consumer b: 将Hello转成小写打印
        对a和b进行合并,合并成c,c里面就保存了这两个操作

 */
public class Demo02Consumer {
    public static void main(String[] args) {
        //调用method方法,传递参数
        method(s -> System.out.println(s.toUpperCase()),
                s -> System.out.println(s.toLowerCase()));
    }

    /*
        定义方法,接收两个Consumer, 第一个Consumer将Hello转成大写打印, 第二个Consumer将Hello转成小写打印
     */
    public static void method(Consumer<String> one, Consumer<String> two) {
        //先使用one将Hello转成大写打印
        //one.accept("Hello");
        //再使用two将Hello转成小写打印
        //two.accept("Hello");
        //将one和two进行合并,合并成新的Consumer,新的Consumer中包含了合并前one和two的操作。
        //合并后的three包含了one和two的操作, 先one后two
        //Consumer<String> three = one.andThen(two);
        //使用three调用accept方法操作数据。
        //three.accept("hello");//相当于先通过one调用accept("hello")再通过two调用                    accept("hello")

        //一步到位,合并之后直接调用accept方法
        one.andThen(two).accept("hello");//one.accept("hello") 再 two.accept("hello");
    }
}

13. Function中的apply方法

/*
    在Java中,还有一个函数式接口叫做Function,这个接口可以叫做函数模型,这个接口可以接收一个数据,并且得到一个结果。
    Function<T,R>要两个泛型, T表示要接收的参数类型, R表示结果的数据类型。

    抽象方法:
        R apply(T t): 接收一个T类型的参数, 得到一个R类型的结果。 比如接收一个字符串类型"10"经过操作,得到10
 */
public class Demo01Function {
    public static void main(String[] args) {
        //调用method方法,传递Lambda表达式。
        method(s -> Integer.parseInt(s));
    }

    /*
        定义方法,使用函数式接口Function当做参数, 这个Function接收一个字符串,得到对应的数字
     */
    public static void method(Function<String, Integer> f) {
        //通过f调用apply方法,将字符串"10",转成数字10然后打印
        Integer num = f.apply("10");
        System.out.println(num);
    }
}

14. Function中的andThen方法

/*
    在Function中有一个默认方法,可以对两个Function进行合并

    默认方法:
        default Function andThen(Function after):对两个Function进行合并。 合并之后有先后顺          序。 比如a.andThen(b)就是先a后b

    举例:
        Function a: "10" -> 10
        Function b:  10  -> 110
        如果对a和b进行合并,合并成c   ->  Function c = a.andThen(b)
        Function c: "10" -> 10 -> 110
 */
public class Demo02Function {
    public static void main(String[] args) {
        method(s -> Integer.parseInt(s), num -> num + 100);
    }
    /*
        定义方法,接收两个Function。
        第一个Function用来将字符串"10"转成数字10
        第二个Function用来将数字10加上100,变成110
     */
    public static void method(Function<String, Integer> one, Function<Integer, Integer> two) {
        //字符串"10"转成数字10
        //Integer num = one.apply("10");
        //数字10加上100,变成110
        //Integer result = two.apply(num);
        //System.out.println("result:" + result);

        //将one和two合并成一个Function
        //合并后的three里面包含了one和two的操作,先one后two
        //Function<String, Integer> three = one.andThen(two);
        //调用apply
        //three是one和two合并的结果, 所以three里面包含了one和two的操作。
        //如果使用three调用apply方法, 先通过one.apply("10"), 再通过two调用apply方法,并传递刚刚one的结果,求出最终结果
        //Integer result = three.apply("10"); //相当于 two.apply(one.apply("10"));
        //System.out.println(result);

        //一步到位
        Integer result = one.andThen(two).apply("10");//相当于                         two.apply(one.apply("10"));
        System.out.println(result);
    }
}

15. Predicate中的抽象方法test的使用

/*
    在Java中,还有一个函数式接口叫做Predicate,这个函数式接口可以判断一个数据是否符合要求。

    抽象方法:
        boolean test(T t): 用来验证一个数据是否符合要求,如果符合要求返回true。

    默认方法:
        default Predicate and(Predicate other):可以对两个Predicate合并,结果是并且的关系。相                                               当于&&
        default Predicate or(Predicate other):可以对两个Predicate合并,结果是或者的关系。相当                                               于||
        default Predicate negate():取反。 相当于!
 */
public class Demo01Predicate {
    public static void main(String[] args) {
        //调用method方法
        method(s -> s.length() == 3);
    }
    /*
        定义一个方法,参数传递Predicate函数式接口。 在方法中判断数据是否符合规则
     */
    public static void method(Predicate<String> p) {
        //进行判断
        boolean flag = p.test("hello");
        System.out.println(flag);
    }
}

16. Predicate中的默认方法and和or

/*
    默认方法
        default Predicate and(Predicate other):可以对两个Predicate合并,结果是并且的关系。相                                              当于&&
        default Predicate or(Predicate other):可以对两个Predicate合并,结果是或者的关系。相当                                              于||

    举例:
        Predicate one: 用来判断字符串"hello"长度是否是3
        Predicate two: 用来判断字符串"hello"是否以hel开头
        如果我们使用and进行合并: Predicate three = one.and(two);
        Predicate three: 及判断字符串的长度,又判断这个字符串的开头,两个判断之间是并且的关系,必须全部成立结果才成立。
 */
public class Demo02Predicate {
    public static void main(String[] args) {
        method(s -> s.length() == 3,
                s -> s.startsWith("hel"));
    }

    /*
        定义方法,传递两个Predicate
        第一个Predicate:判断字符串hello长度是否是3
        第二个Predicate: 用来判断字符串"hello"是否以hel开头
     */
    public static void method(Predicate<String> one, Predicate<String> two) {
        //第一个Predicate:判断字符串hello长度是否是3
       //boolean flagOne = one.test("hello");
       //第二个Predicate: 用来判断字符串"hello"是否以hel开头
       //boolean flagTwo = two.test("hello");
       //要求求出最后结果, 必须one和two都成立,那么最终结果才成立
       //boolean flag = flagOne && flagTwo;
       //System.out.println(flag);

        //直接对两个Predicate使用与(and)进行合并
        //boolean flag = one.and(two).test("hello");//相当于one.test("hello") && two.test("hello")
        //System.out.println(flag);

        //直接对两个Predicate使用或(or)进行合并
        boolean flag = one.or(two).test("hello");//one.test("hello") || twp.test("hello");
        System.out.println(flag);
    }
}

17. Predicate中的默认方法negate

/*
    在Predicate中,还有一个默认方法,可以取反。

    默认方法:
        default Predicate negate():取反。 相当于!
 */
public class Demo03Predicate {
    public static void main(String[] args) {
        //调用method方法,传递Lambda
        method(s -> s.length() == 5);
    }

    /*
        定义一个方法,接收一个Predicate类型的参数。
     */
    public static void method(Predicate<String> p) {
        //使用test方法判断"hello"是否符合要求
        boolean flag = p.negate().test("hello");
        System.out.println("flag:" + flag);
    }
}
  • 资源分享QQ群
  • weinxin
  • 官方微信公众号
  • weinxin
沙海
C语言郝斌老师教程
AnyNode:最便宜的国外服务器
C语言项目源码分享
美女讲师教你学C语言

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: