• 自学Java基础知识第十九天


    day19---JDK8新特性

    1. Lambda表达式

    1. Lambda本质:

       就是一个对象, 指定函数式接口的实现类对象

    1. Lambda表达式使用前提:

       接口必须是函数式接口 : 接口中只有一个抽象方法

       使用注解 @FunctionalInterface 验证和标识接口就是一个函数式接口

    1. Lambda优势:

       大大简化匿名内部类对象实现过程, 从而提高代码的运行效率

    1. Lambda表达式语法结构:

       (参数列表)->{方法体}; // 表达式整体就表示一个函数式接口的实现类对象

    1. 解释Lambda表达式语法:

       因为Lambda表达式只能用于函数式接口的实现, 接口中只有一个抽象方法, 因此Lambda表达式很明确其需要重写方法是哪个

       (参数列表) : 唯一的抽象方法中的参数列表

       -> : 称为Lambda运算符, 也称为箭头运算符

       {方法体} : 表示唯一抽象方法实现过程

    1. Lambda表达式使用的注意事项:

       a : 参数列表: Lambda表达式参数列表中, 只罗列出参数个数和变量名,不需要写参数的数据类型

    1) Lambda表达式重写方法, 没有参数, 小括号就是空的 ()

    2) Lambda表达式重写方法, 只有一个参数, 小括号可以省略

    3) Lambda表达式重写方法, 有多个参数, 按照方法的参数列表, 列出参数名称即可

       b : 方法体:

    1) Lambda表达式重写方法体, 只有一句表达式, 那么大括号可以省略

    2) Lambda表达式重写方法体, 只有一句代码逻辑, 并且这一句就是方法返回值结果, 那么大括号和return关键字可以同时省掉

    代码

    package com.ujiuye.lambda;

    public class Demo01_Lambda表达式语法结构 {

    public static void main(String[] args) {

    // 1.匿名内部类对象实现接口Inter1

    new Inter1() {

    @Override

    public void fun() {

    System.out.println("匿名内部类对象实现方法fun");

    }

    }.fun();

    // 2. Lambda表达式实现接口Inter1

    // (参数列表)->{方法体};// 接口Inter1的实现类对象

    Inter1 t = ()->{

    System.out.println("Lambda表达式实现方法fun");

    };

    t.fun();

    }

    }

    @FunctionalInterface

    interface Inter1{

    public abstract void fun();

    }

    package com.ujiuye.lambda;

    public class Demo02_Lambda表达式使用注意事项 {

    public static void main(String[] args) {

            // 1. 匿名内部类对象实现Inter2

    new Inter2() {

    @Override

    public void print(String s) {

    System.out.println(s + "----");

    }

    }.print("hello");

    // 2. Lambda表达式实现Inter2

    Inter2 i1 = (a)->{

    System.out.println(a + "++++");

    };

    i1.print("world");

    // 3. Lambda表达式实现方法只有一个参数, 参数小括号可以省略

    Inter2 i2 = y -> {System.out.println(y + "--啦啦啦啦");};

    i2.print("123");

    // 4. Lambda表达式实现方法有多个参数

    Inter3 i3 = (a,b)->{

    System.out.println(a + b);

    };

    i3.getSum(3, 5);// 8

    // 5. Lambda表达式实现方法体只有一句代码逻辑, 大括号可以省略

    Inter2 i4 = y->System.out.println(y);

    i4.print("省略大括号");

    // 6. Lambda表达式实现接口Inter4

    Inter4 i5 = (x,y)->{

    boolean boo = x == y;

    System.out.println(boo);

    return boo;

    };

    i5.equal(3, 4);// false

    // 7. Lambda表达式实现接口Inter4, 方法带有返回值类型, 大括号省掉

    Inter4 i6 = (a,b)->a==b;

    System.out.println(i6.equal(3, 3));// true

    }

    }

    @FunctionalInterface

    interface Inter2{

    public abstract void print(String s);

    }

    @FunctionalInterface

    interface Inter3{

    public abstract void getSum(int a, int b);

    }

    @FunctionalInterface

    interface Inter4{

    public abstract boolean equal(int a, int b);

    }

    2. 函数式接口

    2.1 函数式接口的概述

    1. 函数式接口介绍:

       函数式接口中只有一个抽象方法, 使用注解@FunctionalInterface 验证和标识接口就是一个函数式接口 , Lambda表达式只能实现函数式接口

    1. 函数式接口存在意义:

       Java代码定义方法功能, 根据方法实现情况, 需要参数列表, Java方法中的参数列表 数据类型  变量名, 数据类型 变量名..., 方法参数都必须带有指定数据类型 , 但是有一些方法实现过程中, 不仅仅需要固定类型参数, 也需要对于数据的处理方式(处理思想)

       举例 : 定义出一个方法功能, 可以操作两个int类型整数, 实际操作过程按照客户实际需要进行

       客户1 : 做两个整数求和

       客户2 : 做两个整数求差

       客户3 : 做两个整数求乘

    ...

     分析 : 目前方法功能, 不仅仅需要两个整数int, 同时需要提供对各两个数据操作的想法,思想

       数据处理思想,方式, 其实就是对应Java中的一个方法功能,但是Java中不支持将方法作为参数传递, 于是将这个方法封装在一个接口中, 将接口作为方法参数传递, 相当于在传递接口中方法, 传递对于数据的处理思想, 因此函数式接口是为了封装某一个方法而存在

    2.2 JDK提供常用内置函数式接口

    JDK8版本开始, 提供了4个常用函数式接口, 在不同的数据思想操作层面上, 可以直接使用对应的函数式接口, 而不需要自定义, 函数式接口都来自于 java.util.function

    1. 消费型接口:

       Consumer<T>

        void accept(T t) : 提供T类型参数, 在方法中对于参数t进行消费, 消费指对于数据t的处理方式

    1. 供给型接口:

       Supplier<T>

        T get() : get方法就是可以返回一个T类型结果, 不需要任何参数

    1. 函数式接口:

      Function<T,R>

       R apply(T t) : apply方法通过参数t, 获取到结果R

    1. 断言型接口:

      Predicate<T>

       boolean test(T t) : test 方法提供参数t, 验证这个参数是否符合某种规则

    2.3 消费型接口

    Consumer<T>

        void accept(T t) : 提供T类型参数, 在方法中对于参数t进行消费, 消费指对于数据t的处理方式; 如果定义一个方法功能, 不仅仅需要参数t, 还需要对于参数t类型数据的消费方式, Consumer可以作为方法参数进行传递

    案例 : 定义出一个方法功能, 客户预计消费500元现金, 每一个客户对于500元的消费都不同, 将客户的消费方式实现出来

    1) 客户1 : 花了500, 买了一把大宝剑

    2) 客户2 : 花了500, 买了一堆化妆品

    3) 客户3 : 花了500, 买了一双球鞋

    4) .... 还有无限的客户有不同种消费方式

    代码

    package com.ujiuye.function;

    import java.util.function.Consumer;

    public class Demo01_消费型接口 {

    public static void main(String[] args) {

    // 客户1 : 花了500, 买了一把大宝剑

    int money = 500;

    // 调用testConsumer方法, 第二个参数需要一个Consumer接口的实现类

    // 使用Lambda表达式作为Consumer接口的实现类对象

    Consumer<Integer> con = x->System.out.println("花了"+x+",买了一把大宝剑");

    testConsumer(money,con);

    // 2.客户2 : 花了500, 买了一堆化妆品

    Consumer<Integer> con1 = x->{

    if(x >= 500) {

    System.out.println("不能消费,超出预算");

    }else {

    System.out.println("花了"+x+",买了一堆化妆品");

    }

    };

    testConsumer(400,con1);

    }

    /*案例 : 定义出一个方法功能, 客户预计消费500元现金, 每一个客户对于500元的消费都不同,

    将客户的消费方式实现出来

    1)客户1 : 花了500, 买了一把大宝剑

    2)客户2 : 花了500, 买了一堆化妆品

    3)客户3 : 花了500, 买了一双球鞋

    4).... 还有无限的客户有不同种消费方式*/

    /*

     *  分析 :

     *   1) 设定出第一个参数, 表示客户消费的金额

     *   2) 设定出第二个参数, 需要 给出客户对于金额消费方式, 提供Consumer<T>, T泛型表示需要消费的数据

     *

     * */

    public static void testConsumer(int money,Consumer<Integer> con) {

    con.accept(money);

    }

    }

    2.4 供给型接口

    Supplier<T>

        T get() : get方法就是可以返回一个T类型结果, 不需要任何参数 ; 如果定义一个方法功能时, 不需要参数, 只需要获取到某一个类型的数据, 而获取方式多种多样, 可以将Supplier类型作为方法参数传递, 实际传递的是获取某类型数据的思想

    案例 : 定义出一个方法功能, 能给客户创建出一个ArrayList<Integer>类型容器, 容器中装几个数据由客户决定, 容器中承装的数据有什么规律, 由客户决定, 方法主要给客户返回一个符合客户要求的容器

    1) 客户1 : 5个数据, 都是30-80之间的随机数

    2) 客户2 : 8个数据, 1-100之间的随机偶数

    3) ...

    代码

    package com.ujiuye.function;

    import java.util.ArrayList;

    import java.util.Random;

    import java.util.function.Supplier;

    public class Demo02_供给型接口 {

    public static void main(String[] args) {

             // 1) 客户1 : 5个数据, 都是30-80之间的随机数

    Supplier<Integer> sup = ()->new Random().nextInt(51)+30;

    System.out.println(testSupplier(5,sup));

     

    // 2) 客户2 : 8个数据, 1-100之间的随机偶数

    Supplier<Integer> sup1 = ()->{

    Random ran = new Random();

    while(true) {

    int number = ran.nextInt(100)+1;

    if(number % 2 == 0) {// 偶数

    return number;

    }

    }

    };

    System.out.println(testSupplier(8,sup1));

    }

     

    /*案例 : 定义出一个方法功能, 能给客户创建出一个ArrayList<Integer>类型容器,

    容器中装几个数据由客户决定, 容器中承装的数据有什么规律是什么, 由客户决定,

    方法主要给客户返回一个符合客户要求的容器

    1)客户1 : 5个数据, 都是30-80之间的随机数

    2)客户2 : 8个数据, 1-100之间的随机偶数

    3)...*/

     

    /*

     *  分析 :

     *  1. 设计出第一个参数, int n, n就表示容器中装数据个数

     *  2.设计出第二个参数, Supplier<Integer>

     *      Integer get()

     * */

     

    public static ArrayList<Integer> testSupplier(int n,Supplier<Integer> sup){

    ArrayList<Integer> list = new ArrayList<>();

    for(int i = 1; i <= n; i++) {

    // 需要获取一个Integer类型数据

    list.add(sup.get());

    }

    return list;

    }

    }

     

     

     

    2.5 函数型接口

    Function<T,R>

       R apply(T t) : 如果定义一个方法功能时, 目前有参数T t, 需要根据t获取到另外一个类型结果R, 那么Function可以作为方法参数传递

       Function andThen(Function fun1) :

       Function<T,R> fun

       Function<R,W> fun1

       fun.andThen(fun1) : 需求目前具有T数据, 目标数据W, 可是转换过程中, 直接将T类型转换成W比较困难, 于是具有T,先转换成R类型, 再由R转换成W类型, andThen方法功能, 将方法调用者fun的结果R, 作为参数fun1的第一个泛型尽量连续的转换计算

     

     

     案例1 : 定义一个方法功能, 根据整数x,计算出对应整数y, x数据由客户给出, y数据的计算方式根据客户要求决定

    1) 客户1 : yx2

    2) 客户2 : yx相等

    3) 客户3 : yx的平方

    4) ...

     

     案例2 : 定义一个方法功能, 根据字符串x,计算出对应整数y, x数据由客户给出, y数据的计算方式根据客户要求决定

    5) 客户4 : x”6” , 计算出x转换成整数后, 2倍结果

    6) 客户5 : x”-2”, 计算出x转换成整数后, +1的结果

    ...

     

    代码

    package com.ujiuye.function;

    import java.util.function.Function;

    public class Demo03_函数型接口 {

    public static void main(String[] args) {

    // 客户1 : yx2

    Function<Integer,Integer> fun = x->x*2;

    System.out.println(testFunction(5,fun));// 10

     

    // 客户2 : yx相等

    Function<Integer,Integer> fun1 = x->x;

    System.out.println(testFunction(99,fun1));// 99

     

    // 客户3 : yx的平方

    Function<Integer,Integer> fun2 = x->x*x;

    System.out.println(testFunction(3,fun2));// 9

     

    // 客户4 : x为”6, 计算出x转换成整数后, 2倍结果

    Function<String,Integer> fun4 = x->Integer.parseInt(x);

    Function<Integer,Integer> fun5 = x->x*2;

    System.out.println(testFunction1("6",fun4,fun5));// 12

     

    // 客户5 : x为”-2, 计算出x转换成整数后, +1的结果

    Function<Integer,Integer> fun6 = x->x+1;

    System.out.println(testFunction1("-2",fun4,fun6));// -1

    }

     

    /*案例 : 定义一个方法功能, 根据整数x,计算出对应整数y, x数据由客户给出,

    y数据的计算方式根据客户要求决定

    1)客户1 : yx2

    2)客户2 : yx相等

    3)客户3 : yx的平方

    4)...*/

     

    /*

     *   分析 :

     *   1. 设计出一个参数, 就表示目前拥有整数x

     *   2.设计出第二个参数,  Function<Integer,Integer>

     *     Integer apply(Integer)

     * */

     

    public static int testFunction(int x, Function<Integer,Integer> fun) {

    return fun.apply(x);

    }

     

     

    /*案例2 : 定义一个方法功能, 根据字符串x,计算出对应整数y, x数据由客户给出,

    y数据的计算方式根据客户要求决定

    5)客户4 : x为”6, 计算出x转换成整数后, 2倍结果

    6)客户5 : x为”-2, 计算出x转换成整数后, +1的结果

    ...*/

     

    public static int testFunction1(String x,Function<String,Integer> fun,

    Function<Integer,Integer> fun1) {

        return fun.andThen(fun1).apply(x);

    }

    }

     

     

    2.6 断言型接口

    Predicate<T>

       boolean test(T t) : 如果现在具有类型数据T t, 验证这个t是否规则, 可以将Predicate作为方法参数传递

       有两个默认方法功能:

       Predicate and(Predicate pre) : 表示将方法调用者需要规则与方法参数pre的规则, &&判断

       Predicate or(Predicate pre) : 表示将方法调用者需要规则与方法参数pre的规则, ||判断

     

    案例1 :

      定义出一个方法, 需要客户提供一个容器ArrayList<Integer>, 通过客户的意愿, 将容器中符合条件的数据筛选出来, 将筛选出的数据放置在新容器中返回给客户

    1) 客户1 : 要求容器中的所有数, 都能被2整除

    2) 客户2 : 要求所有的数据都不大于100

    3) 客户3 : 要求所有数据都小于100, 并且都是奇数

    4) 客户4 : 要求所有数据或者小于100, 或者是偶数

    5) ...

    代码

    package com.ujiuye.function;

    import java.util.ArrayList;

    import java.util.function.Predicate;

    public class Demo04_断言型接口 {

    public static void main(String[] args) {

    ArrayList<Integer> list = new ArrayList<>();

    list.add(12);

    list.add(15);

    list.add(120);

    list.add(111);

            // 1. 客户1 : 要求容器中的所有数, 都能被2整除

    Predicate<Integer> pre = x->x % 2 == 0;

    System.out.println(testPredicate(list,pre));

    // 2. 客户2 : 要求所有的数据都不大于100

    Predicate<Integer> pre1 = x->x <= 100;

    System.out.println(testPredicate(list,pre1));

    // 3.客户3 : 要求所有数据都小于100, 并且都是奇数

    // Predicate<Integer> pre2 = x->x < 100 && x % 2 != 0;

    Predicate<Integer> pre3 = x->x < 100;

    Predicate<Integer> pre4 = x->x % 2 != 0;

    System.out.println(testPredicate(list,pre3.and(pre4)));

    // 4.客户4 : 要求所有数据或者小于100, 或者是偶数

    System.out.println(testPredicate(list,pre3.or(pre)));

    }

    /*案例 :

      定义出一个方法, 需要客户提供一个容器ArrayList<Integer>,

      通过客户的意愿, 将容器中符合条件的数据筛选出来, 将筛选出的数据放置在新容器中返回给客户

    1)客户1 : 要求容器中的所有数, 都能被2整除

    2)客户2 : 要求所有的数据都不大于100

    3)客户3 : 要求所有数据都小于100, 并且都是奇数

            4)客户4 : 要求所有数据或者小于100, 或者是偶数

    ...*/

    /*

     *  分析 :

     *   1. 设计出第一个参数, ArrayList<Integer>, 就表示客户提供原始集合数据

     *   2. 设计出第二个参数, Predicate<T>

     *      boolean test(T t)

     *

     * */

    public static ArrayList<Integer> testPredicate(ArrayList<Integer> list,Predicate<Integer> pre){

     ArrayList<Integer> newList = new ArrayList<>();

     for(Integer i : list) {

    if(pre.test(i)) {// 符合规则

    newList.add(i);

    }

     }

     return newList;

    }

    }

    2.7 方法引用

    Lambda表达式 : 作为一个函数式接口实现类对象存在

    如果使用Lambda表达式实现一个函数式接口, Lambda(方法体) 已经被某个类型中某个方法实现过了, 那么可以直接引用这个已经实现的方法作为Lambda表达式实现过程

    (Lambda表达式实现一个接口时, A方法已经将需要的实现过程完成好了, 不需要再次将重复的代码在Lambda体中再写一遍, 只需要调用A方法即可)

    注意 : Lambda表达式参数列表和方法体实现上都需要与A方法保持一致

    方法引用:

    1) 函数式接口名 变量 = 类对象 :: 方法名;

    2) 函数式接口名 变量 = 类名 :: 方法名;

    代码

    package com.ujiuye.function;

    public class Demo05_方法引用 {

    public static void main(String[] args) {

            // 1. 使用Lambda表达式实现函数式接口My

    My my = s->System.out.println(s);

    my.print("hello");

    // 2. 使用方法引用实现函数式接口

    My my1 = System.out :: println;

    my1.print("123-------");

    // 3.  使用方法引用实现函数式接口

    // My my2 = s->System.out.println(Integer.parseInt(s)+1);

    My my2 = new Print1() :: print;

    my2.print("115");// 116

    // 4. 使用方法引用实现函数式接口

    My my3 = Print2 :: print2;

    my3.print("10");// 15

    }

    }

    class Print2{

    public static void print2(String s) {

    System.out.println(Integer.parseInt(s)+5);

    }

    }

    class Print1{

    public void print(String s) {

    System.out.println(Integer.parseInt(s)+1);

    }

    }

    @FunctionalInterface

    interface My{

    public abstract void print(String s);

    }

    3. StreamAPI

    1. JDK8版本, Collection单列集合顶层父接口中, 添加一个方法功能stream()

    1) stream() : 表示获取到当前集合的流资源, 返回值类型Stream接口, 证明stream方法返回Stream的一个实现类对象

    2) Stream<T>接口 : 表示流资源, 主要是可以对于单列集合进行操作,访问,筛选相关操作, Stream中的泛型数据就是当前操作集合中的数据类型

    1. Stream中常用方法:

    1) filter(Predicate<T> pre) : 功能将集合中的每一个数据通过参数pre给出的规则进行筛选, 返回值类型Stream<T> , 因此方法调用完毕还可以链式调用

    2) forEach(Consumer<T> con) : 功能遍历Stream中的操作的集合中数据, 参数需要消费型接口, 一边遍历还可以对遍历出元素进行消费性操作

    3) count() : 表示获取到流资源中操作的元素个数, 返回值类型long

    4) skip(long n) : 表示跳过流资源中的前n个数据, 之后的数据继续进行操作, 返回值类型Stream<T> , 因此方法调用完毕还可以链式调用

    5) limit(long n) : 表示保留流资源中前n个数据,之后不操作,返回值类型Stream<T> , 因此方法调用完毕还可以链式调用

    6) map(Function<T,R> fun) : 将流资源中所有的T类型数据转换成R类型数据,返回值类型Stream<T> , 因此方法调用完毕还可以链式调用

    案例 : 定义出一个ArrayList<String>, 集合中存储人物的姓名

    1) 将集合中所有姓张的, 名字有3个字的人名获取到, 并遍历

    2) 跳过前三个人名, 剩下的所有姓张的,名字有2个字的人名获取到, 并遍历

    3) 只留取集合中的前2个人名, 筛选出姓张的人名, 并遍历

    代码

    package com.ujiuye.stream;

    import java.util.ArrayList;

    public class StreamDemo {

    public static void main(String[] args) {

    // 案例 : 定义出一个ArrayList<String>, 集合中存储人物的姓名

           //1. 定义出一个带有人物名字集合

    ArrayList<String> list = new ArrayList<>();

    list.add("张无忌");

    list.add("6");

    list.add("赵敏");

    list.add("周芷若");

    list.add("小昭");

    list.add("张三丰");

    list.add("张成");

    // 1)将集合中所有姓张的, 名字有3个字的人名获取到, 并遍历

    // filter(Predicate<T> pre) : 功能将集合中的每一个数据通过参数pre给出的规则进行筛选

    list.stream().filter(x->x.startsWith("")&&x.length()==3).forEach(

    System.out :: println);

    System.out.println(list.stream().count());// 7

    // 2)跳过前三个人名, 剩下的所有姓张的,名字有2个字的人名获取到, 并遍历

    list.stream().skip(3).filter(x->x.startsWith("")&&x.length()==2).forEach(

    x->System.out.println(x));

    // 3) 只留取集合中的前2个人名, 筛选出姓张的人名, 并遍历

    list.stream().limit(2).filter(x->x.startsWith("")).forEach(System.out :: println);

    // 4) map(Function<T,R> fun) : 将流资源中所有的T类型数据转换成R类型数据,返回值类型Stream<T> , 因此方法调用完毕还可以链式调用

    list.stream().map(x->x+1).forEach(System.out :: print);

    }

    }

  • 相关阅读:
    Kafka入门经典教程【转】
    利用shell找出15分钟内修改的文件
    MySQL性能剖析工具(pt-query-digest)【转】
    Python3安装配置【转】
    查看nginx | apache | php | tengine | tomcat版本的信息以及如何隐藏版本信息【转】
    linux通过sendmail发送邮件
    crontab每10秒钟执行一次
    shell expect的简单用法【转】
    linux中Shell标准输出错误 >/dev/null 2>&1 分析【转】
    Ansible 插件 之 【CMDB】【转】
  • 原文地址:https://www.cnblogs.com/masterhxh/p/13629056.html
Copyright © 2020-2023  润新知