前言

b站java课程学习笔记整理。

b站视频: 黑马程序员全套Java教程_Java基础入门视频教程,零基础小白自学Java必备教程

355. 体验Lambda表达式

函数式编程思想:

尽量忽略面向对象的复杂语法,强调做什么,而不是以什么形式去做。

示例:

package LambdaDemo;

public class Demo {
    public static void main(String[] args) {
        new Thread(() -> {
            System.out.println("启动");
        }).start();
    }
}

356. Lambda表达式的标准格式

package LambdaDemo;

public class Demo {
    public static void main(String[] args) {
        new Thread(() -> {
            System.out.println("启动");
        }).start();
    }
}

Lambda表达式的代码分析

  • ():里面没有内容,可以看成是方法形式参数为空
  • ->:用箭头指向后面要做的事
  • {}:包含一段代码,我们成为代码块,可以看成是方法体中的内容。

三要素:形参,箭头,代码块。

  • Lambda表达式的格式:(形式参数)->{代码块}
  • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可。
  • ->:固定写法,代表指向动作。
  • 代码块: 具体要做的事,也就是以前我们写的方法体的内容。

357. Lambda表达式的练习1(抽象方法无参无返回值)

使用前提:

  • 有一个接口
  • 这个接口中有且仅有一个抽象方法
package Lambda练习1;

public class EatableImpl implements Eatable{
    @Override
    public void eat() {
        System.out.println("吃吃吃");
    }
}

package Lambda练习1;

public class Demo {
    public static void main(String[] args) {
        Eatable e = new EatableImpl();
        useEatable(e);
        useEatable(()->{
            System.out.println("吃饭");
        });
    }
    private static void useEatable(Eatable e){
        e.eat();
    }
}

358. Lambda表达式的练习2(抽象方法带参无返回值)

package Lambda练习2;

public interface Flyable {
    void fly(String s);
}

package Lambda练习2;

public class FlyDemo {
    public static void main(String[] args) {
        useFlyable((String s) ->{
            System.out.println(s);
            System.out.println("飞机自驾游");
        });
    }
    private static void useFlyable(Flyable f){
        f.fly("风和日丽");
    }

}

359. Lambda表达式的练习3(抽象方法带参带返回值)

package Lambda练习3;

public interface Addable {
    int add(int x, int y);
}

package Lambda练习3;

public class AddDemo {
    public static void main(String[] args) {
        useAddable((int x, int y) -> {
            //return x + y;
            //这个就类似于方法体了。
            return x-y;
        });
    }

    public static void useAddable(Addable a) {
        int sum = a.add(10, 20);
        System.out.println(sum);
    }
}

360. Lambda表达式的省略模式

  • 参数类型可以省略。但是有多个参数的情况下不能只省略一个。
  • 如果参数有且仅有一个,那么小括号可以省略。
  • 如果代码块的语句只有一条,可以省略大括号和分号,甚至return。

361. Lambda表达式的注意事项

  • 使用Lambda一定要有接口,并且接口中有且仅有一个抽象方法。
  • 必须有上下文环境。
    • 根据局部变量的赋值Runnable r =() ->System.out.println("Lambda表达式");
    • 根据调用方法的参数new Thread(()->System.out.println("lambda表达式")).start();

362. Lambda表达式和匿名内部类的区别

所需类型不同

  • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类。
  • Lambda表达式:只能是接口

使用限制不同

  • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类。
  • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式

实现原理不同

  • 匿名内部类:编译后,产生一个单独的.class字节码文件。
  • Lambda:编译后没有.class文件。对应的字节码会在运行时动态生成。

363. 接口组成更新

接口的组成

  • 常量
    • public static final
  • 抽象方法
    • public abstract
  • 默认方法(Java 8)
  • 静态方法(Java 8)
  • 私有方法(Java 9)

364. 接口中的默认方法

java中提供的更新接口的默认方法default。添加默认方法时不会强制让实现类重写这个方法(不过也可以重写)。

示例:

default void show3(){
    System.out.println("show3");
}

365. 接口中静态方法

使用static关键字修饰。

只能同故宫接口名去调用。和static的成员方法类似。public可以省略

例子:

static void test(){
    System.out.println("静态方法")
}

366. 接口中私有方法

  • 格式1:private 返回值类型 方法名(参数列表){}

​ 范例1:private void show(){}

  • 格式2:private static 返回值类型 方法名(参数列表){}

​ 范例2:private static void method(){}

可以是普通的私有方法,也可以是静态的私有方法。

私有方法是用来将接口中的各种方法的共同点抽象出来,简化代码的书写。

注意事项:

  • 默认方法可以调用私有的静态方法和非静态方法。
  • 静态方法只能调用私有的静态方法

367. 方法引用

通过方法引用来使用已经存在的方案。

方法引用符:::

package Lambda练习2;

public class FlyDemo {
    public static void main(String[] args) {
        // 把参数传递给了println方法
        useFlyable(System.out::println);
    }
    private static void useFlyable(Flyable f){
        f.fly("风和日丽");
    }

}

原则: 可推导的就是可省略的。

368. 方法引用符

  • ::该符号位引用运算符,他所在的表达式称为方法引用。
  • 可推导的就是可省略的。
  • 方法引用是Lambda的孪生兄弟
package 方法引用符;

public interface Printable {
    void printInt(int i);
}

package 方法引用符;

public class Demo {
    public static void main(String[] args) {
        usePrintable(System.out::println);
    }

    public static void usePrintable(Printable p){
        p.printInt(666);
    }
}

369. 引用类方法

引用类方法,其实就是引用类的静态方法。

  • 格式:类名::静态方法
  • 范例:Integer::parseInt

370. 引用对象的实例方法

格式:对象::成员方法

范例:"Hello world"::toUpperCase

371. 引用类的实例方法

格式:类名::成员方法

范例:String :: substring

372. 引用构造器

其实就是引用构造方法。

格式:类名::new

范例:Student::new

373. 函数式接口

有且仅有一个抽象方法的接口。(即lambda表达式的使用前提)

如何检测一个接口是不是函数式接口?

  • @FunctionalInterface 用来检查。就类似于@Override

374. 函数式接口作为方法的参数

如果方法的参数是一个函数式接口,可以使用Lambda表达式作为参数传递。

  • startThread(()->System.out.println(Thread.currentThread().getName()+"启动"));

375. 函数式接口作为方法的返回值

如果方法的返回值是一个函数式接口,可以使用Lambda表达式作为结果返回

private static Comparator<String>getComparator(){
    return (s1,s2)->s1.length - s2.length();
}

376.函数式接口之supplier

Supplier<T>:包含一个无参的方法。

  • T get(): 获得结果
  • 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
  • Supplier<T>接口也被称为生产型接口,如果制定了泛型是什么类型,那么接口中的get方法就会产生什么类型的数据。
package supplier使用;

import java.util.function.Supplier;

public class Demo {
    public static void main(String[] args) {
        String s = getString(()-> "林青霞");
        System.out.println(s);
    }
    private static String getString(Supplier<String> sup){
        return sup.get();
    }
}

377. supplier接口之获取最大值

package supplier使用;

import java.util.function.Supplier;

public class Demo {
    public static void main(String[] args) {
        int[] arr = {1, 2, 23, 5, 6};
        int maxValue = getMax(() -> {
            int max = arr[0];
            for (int i = 1; i < arr.length; i++) {
                if (arr[i] > max) {
                    max = arr[i];
                }
            }
            return max;
        });
        System.out.println(maxValue);
    }

    public static int getMax(Supplier<Integer> sup) {
        return sup.get();

    }
}

378. Consumer接口

Consumer<T>:包含两个方法。

  • void accept(T t): 对给定的参数执行此操作
  • default Consumer <T> andThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作。
  • Consumer<T>接口也被称为消费型接口。
package Consumer接口;

import java.util.function.Consumer;

public class Demo {
    public static void main(String[] args) {
        operatorString("dasdsa", System.out::println, s-> System.out.println(new StringBuilder(s).reverse()));
    }
    private static void operatorString(String name, Consumer<String> con1,Consumer<String> con2){
        con1.andThen(con2).accept(name);
    }
}

消费就是对数据执行一次操作。

379. 练习:按要求打印信息

  • String[] strArray = {"林青霞,30","张曼玉,35","王祖贤,33"};
  • 按照姓名:XX,年龄:XX的格式把信息打印出来。
  • 要求:
    • 把打印姓名的动作作为第一个Consumer接口的Lambda实例。
    • 把打印年龄的动作作为第二个Consumer接口的Lambda实例。
    • 将两个Consumer几口按照顺序组合到一起使用。
package Consumer接口;

import java.util.function.Consumer;

public class Demo2 {
    public static void main(String[] args) {
        String[] strArray = {"林青霞,30","张曼玉,35","王祖贤,33"};

        printInfo(strArray,str -> System.out.print("姓名:"+str.split(",")[0]),str->
                System.out.println(",年龄"+Integer.parseInt(str.split(",")[1])));
    }
    private static void printInfo(String[] strArray, Consumer<String> con1,Consumer<String> con2){
        for(String str : strArray){
            con1.andThen(con2).accept(str);
        }
    }
}

380. predicate接口1

Consumer<T>:常用四个方法。

  • boolean test(T t):对给定的参数进行判断,判断逻辑由Lambda表达式实现,返回一个布尔值。
  • default Predicate<T> negate(): 返回一个逻辑的否定,对应逻辑非。
  • default Predicate<T> and(Predicate other):返回一个短路与的组合判断。
  • default Predicate<T>or(Predicate other):返回一个组合判断,对应短路或。
  • Predicate<T>接口通常用于判断参数是否满足指定的条件。

示例代码:

package PredicateDemo;

import java.util.function.Predicate;

public class Demo {
    public static void main(String[] args) {
        boolean b1 = checkString("hello",s-> s.length()>8);
        System.out.println(b1);
    }
    private static boolean checkString(String s, Predicate<String> pre){
        return pre.test(s);
    }
}

381. predicate接口2

package PredicateDemo;

import java.util.function.Predicate;

public class Demo {
    public static void main(String[] args) {
        boolean b1 = checkString("helloworld",s-> s.length()>8,s->s.length()<15);
        System.out.println(b1);
    }
    private static boolean checkString(String s, Predicate<String> pre1,Predicate<String> pre2){
        return pre1.and(pre2).test(s);
    }
}

382. 练习:筛选满足条件的数据

  • String[] strArray = {"林青霞,30","柳岩,34","张曼玉,35","貂蝉,31","王祖贤,33"};

  • 使用predicate接口,将符号要求的字符串筛选到集合ArrayList中。

  • 同时满足以下要求: 姓名长度大于2,年龄大于33

  • 分析:

    使用and方法连接判断条件。

package predicate应用;

import java.util.ArrayList;
import java.util.function.Predicate;

public class Demo {
    public static void main(String[] args) {
        String[] strArray = {"林青霞,30", "柳岩,34", "张曼玉,35", "貂蝉,31", "王祖贤,33"};
        ArrayList<String> list = myFilter(strArray, s -> s.split(",")[0].length() > 2,
                s -> Integer.parseInt(s.split(",")[1]) > 33);
        for(String str:list){
            System.out.println(str);
        }
    }

    public static ArrayList<String> myFilter(String[] strArray, Predicate<String> pre1, Predicate<String> pre2) {
        ArrayList<String> array = new ArrayList<>();
        for (String str : strArray) {
            if (pre1.and(pre2).test(str)) {
                array.add(str);
            }
        }
        return array;
    }
}

383. Function接口

Function<T,R>:常用两个方法。

  • R apply(T t)将此函数应用于给定的参数
  • default <V> Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果。

384. Function接口的应用

package predicate应用;


import java.util.function.Function;


public class Demo {
    public static void main(String[] args) {
        String s = "林青霞,33";
        convert(s,name->name.split(",")[1], Integer::parseInt,i->i+70);
    }
    private static void convert(String s, Function<String, String>fun1,Function<String, Integer>fun2,Function<Integer, Integer>fun3){
        int i = fun1.andThen(fun2).andThen(fun3).apply(s);
        System.out.println(i);
    }
}