一、lamdba表达式基础知识

1、lamdba基本知识

Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

使用 Lambda 表达式可以使代码变的更加简洁紧凑。

2、lamdba的优点、为什么要是使用lamdba

在Java中,如果不使用lambda,那么恐怕就只能选择用匿名内部类来应付类似的场景。

首先看以下这个例子:

class Foobar {
    Runnable foo() {
        return new Runnable() {
            @Override
            public void run() {
            }
        };
    }
    Runnable bar() {
        return () -> {};
    }
}

同样的功能,两种写法,但是结果并不是相同的。

首先,第一种是一个非静态匿名内部类,这样的一个类它会在编译期额外定义一个类,这个类的每个实例都将会持有外部实例的强引用。

这样的结果就会导致,如果这个匿名类的实例没有被销毁,那么外部类的实例也不能够被销毁,于是就有可能发生内存泄漏,比如当某Handler持有Activity的强引用并且没有被GC回收时,结果导致Activity即使destroy之后也不能被GC回收,这就是一种典型的内存泄漏的场景。

而对于上面这个lambda来说,由于它并没有捕获this,因此它并不会持有this的引用,即使经过了Desuger,仍旧不会捕获this的强引用,所以不会因此而发生内存泄漏。

其次,对于上面这种情况来说,由于没有任何变量需要被捕获,因此每次调用实际上根本没有必要重复创建新的对象,而用lambda就可以做到这一点,上面这个lambda每次返回的都是同一个实例的引用。

最后,相比起以上两条来说,这一条显得无关紧要。由于匿名类也是类,所以它经过编译后也要有自己的class文件,而在每个class文件中的常量表都会占据相当一部分空间,而如果是用lambda代替匿名类,则不会在编译期生成这些class文件,于是二进制文件占据的空间会稍微减少,不过如果是在android开发中,这种区别将会变得更加微不足道,因为dex已经为了这种情况而节省了大量的空间出来,dex数量远远小于class数量因此常量表也会被更多地复用,所以lambda与匿名类在包大小上的区别实际并不明显。

3、lamdba的基本语法

()->{}

语法精简的规则,可以看下面

4、什么时候使用

  • 一个lambda表达式是一个带参数的代码块
  • 当你想要代码块在以后的某个时间点执行,就可以使用lambda表达式
  • lambda表达式可以被转换为函数式接口
  • lambda表达式可以在闭包作用域中有效的访问final变量
  • 方法和构造器引用可以引用构造器和方法,但无效调用他们,我们使用这种新的语法编码更加的简单。 这个就是通过直接调用构造方法创建对象,更加便捷方便

二、lamdba实战

1、创建接口

@FunctionalInterface public interface lamdbaInterface1 { } 作用:只允许该接口有一个实现方法,(因为jdk1.8新特性,出现了default关键字,这个导致接口中存在方法体了,那么就)不可以有多个需要实现的方法!! 原因:根据上面lamdba的规则就可以知道,能够用lamdba表达式,那个这个接口一定是只有一个实现方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gwrmiRq2-1647686788528)(img/image-20220319090719395.png)]

比如上面这种情况,只要有俩个方法,必定报错

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x80lNwv7-1647686788529)(img/image-20220319090742802.png)]

上面这种情况就没有问题

  1. 无返回值、无参数的接口
  2. 无返回值、一个参数的接口
  3. 无返回值、多个参数的接口
  4. 有返回值、无参数的接口
  5. 有返回值、有一个参数的接口
  6. 有返回值、有多个参数的接口
  7. 返回值是对象的多参数接口
  8. 返回值是集合的多参数接口

2、语法精简:

  1. 为什么可以精简
  2. 怎么精简
  3. 精简的好处有事什么?

为什么可以精简:

  • 比如我们在写普通的lambda表达式的时候,人家有返回值,那咱们方法体里面也一定要有返回值,那么问题来了,你想过没有,为啥你一定要有返回值呢?你写返回值的依据是什么呢?我们写这个方法,最后的返回值的依据就是根据接口
  • 接口就是一套规范,尽管他没有方法体,也不实现具体的业务逻辑,但是他规范了我们的参数类型、规范了我们的返回值类型、规范了我们的方法名,所以我们的返回值也就是根据接口来确定的!
  • 上面就是我们可以精简的原因,因为接口已经规范过了,所以有些参数类型,在lambda中我们不写也是可以的,因为接口已经规范过了,我们已经知道应该是什么类型了同理,其他能够精简的地方,也一定是基于这个原理,接口已经规范过了, 你不说,我也明白,所以你可以不写

怎么精简?

  • 参数类型可以省略(备注:``如果要省略参数类型,每一个参数类型都要省略`)
  • 参数中的小括号:如果参数中只有一个参数,那么这个小括号也是可以省略的!
  • 方法大括号:
if(true)
    System.out.println("如果if下只有一个表达式,那么if 的大括号是可以省略的")

同理,在lambda中也是适用的,如果你只有方法体中只有一条输出语句,那么大括号也是可以省略的!!!

如果方法体中只有一个返回值,那么,返回值的return关键字是可以去掉的!!lamdbaInterface6 interface6 = (a, b) -> "城市与";
    System.out.println(interface6.t6(12, "城市与dsaf"));

    lamdbaInterface7 interface2 = (a , b)-> new User(10);
    System.out.println(interface2.t7(1, "jaj"));

精简的好处

  • 代码更加精简了

3、方法(普通方法和构造方法)引用

语法:

方法的隶属者::方法名

  1. 静态方法,方法隶属者就是他的类
  2. 普通方法,方法隶属者就是他的对象
  3. 构造方法:方法隶属者就是他的类

注意点:

  1. 参数数量和类型一定要和接口中定义的方法一致
  2. 返回值的类型一定要和接口中定义的方法一直

4、小知识

这个知识点有点忘记了

12 除 10 , 得到1 余 2

如果用 /符号,则得到1 取整

如果用%符号,则得到2 取余

System.out.println(12 / 10);   // 1
        System.out.println(1 / 10);    // 0
        System.out.println(30 / 10);   // 3
        System.out.println(12 % 10);   // 2
        System.out.println(20 % 10);   // 0

三、综合案例一(集合排序)

集合排序,使用Comprator接口

  1. 使用内部类
  2. 使用lambda

比较list集合中对象的大小关系,核心代码如下:

/*下面这三种都是可以的*/
list.sort(new Comparator<User>() {
    @Override
    public int compare(User o1, User o2) {
        return o1.getAge() - o2.getAge();
    }
});

list.sort((o1,o2)->{
    return o1.getAge() - o2.getAge();
});


list.sort(Comparator.comparingInt(User::getAge));

全部代码如下:

package com.chengshiyu.app;

import com.chengshiyu.pojo.User;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Comparator;

/**
 * @author 程世玉
 * @create 2022/3/19 14:54
 * @PROJECT_NAME JavaSE
 * @Description
 */
public class main4 {

    /*集合排序,对于对象进行排序*/
    @Test
    public void t1() {
        ArrayList<User> list = new ArrayList<>();

        User user1 = new User(18, "程世玉");
        User user2 = new User(1, "w");
        User user3 = new User(8, "j");
        User user4 = new User(128, "c");
        User user5 = new User(18, "s");
        User user6 = new User(188, "y");
        User user7 = new User(10, "l");
        User user8 = new User(118, "o");
        User user9 = new User(13, "v");

        list.add(user1);
        list.add(user2);
        list.add(user3);
        list.add(user4);
        list.add(user5);
        list.add(user6);
        list.add(user7);
        list.add(user8);
        list.add(user9);


        /*下面这三种都是可以的*/
        list.sort(new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return o1.getAge() - o2.getAge();
            }
        });

        list.sort((o1,o2)->{
            return o1.getAge() - o2.getAge();
        });


        list.sort(Comparator.comparingInt(User::getAge));



        for (User user : list) {
            System.out.println(user);
        }
    }
}

四、综合案例二(TreeSet进行排序)

比较对象,一定要重写Compartaor接口,因为set集合是没有办法直接比较两个对象的大小的

代码如下:

从大到小排,那就遇见大于,返回-1即可

从小到大排,那就遇见小于,返回-1即可

public static void main(String[] args) {
    TreeSet<User> set = new TreeSet<User>((o1, o2)->{
        if (o1.getAge() >= o2.getAge()){
            /*前面的比后面的大,我们不想让大的排后面,就返回-1 false即可*/
            return -1;
        }else {
            return 1;
        }
    });

    User user1 = new User(18, "程世玉");
    User user2 = new User(1, "w");
    User user3 = new User(8, "j");
    User user4 = new User(128, "c");
    User user5 = new User(18, "s");
    User user6 = new User(188, "y");
    User user7 = new User(10, "l");
    User user8 = new User(118, "o");
    User user9 = new User(13, "v");

    set.add(user1);
    set.add(user2);
    set.add(user3);
    set.add(user4);
    set.add(user5);
    set.add(user6);
    set.add(user7);
    set.add(user8);
    set.add(user9);

    System.out.println(set);
}

五、综合案例五(forEach遍历)

  1. 全部输出怎么输出
  • 增强for
  • list.forEach(System.out::println);
  1. 选择性输出怎么输出 (如下)

核心代码:

list.forEach(e -> { if (e.getAge() > 18){ System.out.println(e); } });

全部代码

package com.chengshiyu.app;

import com.chengshiyu.pojo.User;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.TreeSet;

/**
 * @author 程世玉
 * @create 2022/3/19 16:48
 * @PROJECT_NAME JavaSE
 * @Description
 */
public class main6 {
    public static void main(String[] args) {
        ArrayList<User> list = new ArrayList<>();

        User user1 = new User(18, "程世玉");
        User user2 = new User(1, "w");
        User user3 = new User(8, "j");
        User user4 = new User(128, "c");
        User user5 = new User(18, "s");
        User user6 = new User(188, "y");
        User user7 = new User(10, "l");
        User user8 = new User(118, "o");
        User user9 = new User(13, "v");

        list.add(user1);
        list.add(user2);
        list.add(user3);
        list.add(user4);
        list.add(user5);
        list.add(user6);
        list.add(user7);
        list.add(user8);
        list.add(user9);

        for (User user : list) {
            System.out.println(user);
        }
        System.out.println("=================================");
        list.forEach(System.out::println);
        System.out.println("=================================");

        /*筛选出年龄大于18的*/
        list.forEach(e -> {
            if (e.getAge() > 18){
                System.out.println(e);
            }
        });
    }
}

六、综合案例六(removeif实现)

核心代码:

/*1、使用迭代器方式*/ Iterator<User> iterator = list.iterator(); while (iterator.hasNext()){ User next = iterator.next(); if (next.getAge() > 18){ /*只要年龄大于18,就去除*/ iterator.remove(); } } /*2、使用lambda表达式*/ list.removeIf(e -> e.getAge() > 10);

全部代码

public static void main(String[] args) {
    ArrayList<User> list = new ArrayList<>();

    User user1 = new User(18, "程世玉");
    User user2 = new User(1, "w");
    User user3 = new User(8, "j");
    User user4 = new User(128, "c");
    User user5 = new User(18, "s");
    User user6 = new User(188, "y");
    User user7 = new User(10, "l");
    User user8 = new User(118, "o");
    User user9 = new User(13, "v");

    list.add(user1);
    list.add(user2);
    list.add(user3);
    list.add(user4);
    list.add(user5);
    list.add(user6);
    list.add(user7);
    list.add(user8);
    list.add(user9);


    /*1、使用迭代器方式*/
    Iterator<User> iterator = list.iterator();
    while (iterator.hasNext()){
        User next = iterator.next();
        if (next.getAge() > 18){
            /*只要年龄大于18,就去除*/
            iterator.remove();
        }
    }

    /*2、使用lambda表达式*/
    list.removeIf(e -> e.getAge() > 10);

七、综合案例五(线程实例化实现)

public static void main(String[] args) {
    Thread thread = new Thread(()->{
        for (int i = 0; i < 100; i++){
            System.out.print(i);
        }
    });

    thread.start();
}

八、Java提供的内置接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dE3Elc32-1647686788530)(img/image-20220319123329054.png)]

其实仔细观察,这个内置的接口,在我们前面写的方法中已经用到了

看源码:

1、list.removeIf方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-exp6JaIc-1647686788531)(img/image-20220319173921652.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T0znmk3e-1647686788532)(img/image-20220319173926690.png)]

2、foreach方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vItiIrxO-1647686788533)(img/image-20220319174002816.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UZreDJRr-1647686788534)(img/image-20220319174008261.png)]

3、关于TreeSet中实现的接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DdDBIzb6-1647686788535)(img/image-20220319174043516.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pbc8s47X-1647686788536)(img/image-20220319174048875.png)]

九、闭包问题

案例一:

public static void main(String[] args) {
    Supplier<Integer> number = getNumber();
    System.out.println(number.get());



}

public static Supplier<Integer> getNumber(){
    int a = 10;
    int b = 11;

    return ()-> a + b;
}

案例二

public static void main(String[] args) { int a = 10; // Consumer<Integer> c = System.out::println; /*闭包,这个时候可以引用外面的参数*/ Consumer<Integer> c = (e)->{ /*闭包,这个时候可以引用外面的参数*/ /*本来是可以用上面那中方式的,但是通过这种方式能够更加清晰看到结果,假如我们传入的a,那到底是a传进去的还是他这里面本来就可以直接引入外界的a,通过这种方式能够很清楚的看到*/ System.out.println(a); }; c.accept(a); }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5hlKnhZw-1647686788537)(img/image-20220319184522520.png)]

十、Comparator 和 Comparable 接口

1、Comparable

Comparable是排序接口。若一个类实现了Comparable接口,就意味着该类支持排序。实现了Comparable接口的类的对象的列表或数组可以通过Collections.sort或Arrays.sort进行自动排序。

作用:

此接口只有一个方法compare,比较此对象与指定对象的顺序,如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。

实例:我们有两个User对象,我们如何来比较二者的大小呢?我们可以通过User实现Comparable接口来实现

代码如下:

实体类实现Comparable接口,重写CompareTo方法,定义的比较对象规则就是比较两个对象的年龄,哪个年龄大哪个对象就大

返回值规则:

  • 小于则返回负数
  • 等于则返回0
  • 大于则返回正数
@Data
@AllArgsConstructor
@NoArgsConstructor
public class t1 implements Comparable<t1>{
    private Integer age;
    @Override
    public int compareTo(t1 o) {
        /*小于则返回负数,等于则返回0 大于则返回正数*/
        return this.age - o.age;
    }
}

测试

public class Demo01 {
    @Test
    public void test(){
        t1 a = new t1(18);
        t1 b = new t1(17);

        /*比较量对象的年龄*/
        int i = a.compareTo(b);
        System.out.println(i);

    }
}

2、Comparator

Comparator是比较接口,我们如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序,这个“比较器”只需要实现Comparator接口即可。也就是说,我们可以通过实现Comparator来新建一个比较器,然后通过这个比较器对类进行排序

Comparator源码如下:

package java.util;
public interface Comparator<T>
 {
    int compare(T o1, T o2);
    boolean equals(Object obj);
 }
案例一:使用匿名内部类方式
@Test
public void t1() {
    ArrayList<User> list = new ArrayList<>();

    User user1 = new User(18, "程世玉");
    User user2 = new User(1, "w");
    User user3 = new User(8, "j");
    User user4 = new User(128, "c");
    User user5 = new User(18, "s");
    User user6 = new User(188, "y");
    User user7 = new User(10, "l");
    User user8 = new User(118, "o");
    User user9 = new User(13, "v");

    list.add(user1);
    list.add(user2);
    list.add(user3);
    list.add(user4);
    list.add(user5);
    list.add(user6);
    list.add(user7);
    list.add(user8);
    list.add(user9);


    /*下面这三种都是可以的*/
    list.sort(new Comparator<User>() {
        @Override
        public int compare(User o1, User o2) {
            return o1.getAge() - o2.getAge();
        }
    });

案例二、采用lambda表达式

public void t1() {
        ArrayList<User> list = new ArrayList<>();

        User user1 = new User(18, "程世玉");
        User user2 = new User(1, "w");
        User user3 = new User(8, "j");
        User user4 = new User(128, "c");
        User user5 = new User(18, "s");
        User user6 = new User(188, "y");
        User user7 = new User(10, "l");
        User user8 = new User(118, "o");
        User user9 = new User(13, "v");

        list.add(user1);
        list.add(user2);
        list.add(user3);
        list.add(user4);
        list.add(user5);
        list.add(user6);
        list.add(user7);
        list.add(user8);
        list.add(user9);


        /*下面这三种都是可以的*/
        list.sort(new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return o1.getAge() - o2.getAge();
            }
        });


//        list.sort(Comparator.comparingInt(User::getAge));

        list.sort((o1,o2)->{
            return o1.getAge() - o2.getAge();
        });

        for (User user : list) {
            System.out.println(user);
        }
    }

3、Comparable和Comparator区别比较

Comparable是排序接口,若一个类实现了Comparable接口,就意味着“该类支持排序”。而Comparator是比较器,我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。

说人话,最简单的理解就是

  • Comparable接口必须实现ComparateTo方法,根据这个方法,就可以判断出来,是这个跟对象跟谁比较,那也就是说这个对象自己有排序的规则,我自己就有规则能够判断我这个对象比你大还是比你小,所以这个接口一般是应用在两个对象自己进行比较
  • Comparator接口里面有Comparator方法,根据源码,根据人家这个方法,就很容易判断理解出来,这个接口其实是对两个对象的比较,也就是说他相当于一个裁判,判决两个对象哪个大,哪个小,
  • Comparator应用一般是在list集合中等集合中,为啥呢,list集合中可以放各种类型的对象,如果要对list集合中的对象进行排序,list再要求每个对象都要有自己的排序规则不现实,所以Comparator应运而生,这时候list就是裁判了,他有了判决的标准了,所以可以看一下list中的sort方法,就是实现的这个Comparator接口!!!!

问题:

问题一

public String t6(int age, String name) {
    return age +"左边 int  右边 String 两个相加极为String" + name;
}

返回值是String 类型,为啥一个int类型 + String类型 就可以转换为String类型,为啥?什么原理?

问题二

闭包,为什么就可以引用外界的局部变量呢?

匿名内部类是不是也是可以的?