一、Lambda表达式

使用Lambda表达式,实际就是创建出该接口的实例对象。

1.1 创建线程

使用Labmda表达式需要函数式编程接口。

@Test
public void test1() {
    // 匿名内部类创建线程!
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("匿名内部类创建线程!");
        }
    }).start();
    // Lambda创建线程
    new Thread(() -> System.out.println("Lambda创建线程")).start();
}
// 表达式
() -> {}
// 方法参数
()
// 方法要实现的内容,具体实现只有一段代码可以省略{}
{}
// 总结
使用Lambda表达式创建线程的时候,并不关心接口名,方法名,参数名。只关注他的参数类型,参数个数,返回值。

二、Stream流

2.1 集合操作

排序一个集合并遍历。

@Test
public void test1() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    list.stream().sorted((i1, i2) -> i2 - i1).forEach((System.out::println));
}

结果

10
9
8
7
6
5
4
3
2
1

常用操作

@Test
public void test2() {
    List<StreamVo> streamVos = new ArrayList<>();
    streamVos.add(new StreamVo("啊吴", 2, "男"));
    streamVos.add(new StreamVo("啊多", 3, "女"));
    streamVos.add(new StreamVo("啊强", 44, "男"));
    streamVos.add(new StreamVo("小吴", 11, "女"));
    streamVos.add(new StreamVo("小多", 22, "女"));
    streamVos.add(new StreamVo("小小强", 33, "女"));
    streamVos.add(new StreamVo("小强强", 33, "男"));

    // 找出姓小的
    List<StreamVo> streamVos1 = new ArrayList<>();
    for (StreamVo streamVo : streamVos) {
        if (streamVo.getName().startsWith("小")) {
            streamVos1.add(streamVo);
        }
    }
    System.out.println(streamVos1);

    // 接着找出名字为3个的
    List<StreamVo> streamVos2 = new ArrayList<>();
    for (StreamVo streamVo : streamVos1) {
        if (streamVo.getName().length() == 3) {
            streamVos2.add(streamVo);
        }
    }
    System.out.println(streamVos2);

    // 接着找出性别为男的
    List<StreamVo> streamVos3 = new ArrayList<>();
    for (StreamVo streamVo : streamVos2) {
        if (streamVo.getSex().equals("男")) {
            streamVos3.add(streamVo);
        }
    }
    System.out.println(streamVos3);

    // 接着遍历
    for (StreamVo streamVo : streamVos3) {
        System.out.println(streamVo);
    }

    // Stream流使用
    streamVos.stream()
        .filter(streamVo -> streamVo.getName().startsWith("小"))
        .filter(streamVo -> streamVo.getName().length() == 3)
        .filter(streamVo -> streamVo.getSex().equals("男"))
        .forEach(System.out::println);

    // 找出姓小的
    List<StreamVo> toList1 = streamVos.stream().filter(streamVo -> streamVo.getName().startsWith("小")).collect(Collectors.toList());
    System.out.println(toList1);

    // 找出姓小的, 年龄大于30的
    List<StreamVo> toList2 = streamVos.stream().filter(streamVo -> streamVo.getName().startsWith("小") && streamVo.getAge() > 30).collect(Collectors.toList());
    System.out.println(toList2);

    // 获取姓名的集合
    List<String> toList3 = streamVos.stream().map(StreamVo::getName).collect(Collectors.toList());
    System.out.println(toList3);
}

结果

[StreamVo{name='小吴', age=11, sex='女'}, StreamVo{name='小多', age=22, sex='女'}, StreamVo{name='小小强', age=33, sex='女'}, StreamVo{name='小强强', age=33, sex='男'}]

[StreamVo{name='小小强', age=33, sex='女'}, StreamVo{name='小强强', age=33, sex='男'}]

[StreamVo{name='小强强', age=33, sex='男'}]

StreamVo{name='小强强', age=33, sex='男'}

StreamVo{name='小强强', age=33, sex='男'}

[StreamVo{name='小吴', age=11, sex='女'}, StreamVo{name='小多', age=22, sex='女'}, StreamVo{name='小小强', age=33, sex='女'}, StreamVo{name='小强强', age=33, sex='男'}]

[StreamVo{name='小小强', age=33, sex='女'}, StreamVo{name='小强强', age=33, sex='男'}]

[啊吴, 啊多, 啊强, 小吴, 小多, 小小强, 小强强]

2.2 map与peek的区别

peek用来修改数据,map用来转换数据类型,peek接收一个没有返回值的λ表达式,可以做一些输出,外部处理等。map接收一个有返回值的λ表达式,之后Stream的泛型类型将转换为map参数λ表达式返回的类型。

三、函数式编程接口

特点:

1. Lambda表达式。

2.函数式编程接口的特点是只有一个抽象方法。

3.@FunctionalInterface注解。

3.1 常用的函数式编程接口

一元接口表示只有一个入参,二元接口表示有两个入参。

测试

@Test
public void test2() {
    System.out.println("----------------------Function--------------------");
    // Function 一个入参一个返回值
    Function<String, Object> function = s -> s + "一个入参一个返回值";
    Object functionResult = function.apply("Function");
    System.out.println(functionResult);

    System.out.println("----------------------BiFunction--------------------");
    // BiFunction 两个入参一个返回值
    BiFunction<String, String, String> biFunction = (s1, s2) -> s1 + s2;
    String biFunctionResult = biFunction.apply("参数1", "参数2");
    System.out.println(biFunctionResult);

    System.out.println("----------------------Consumer--------------------");
    // Consumer 一个入参没有返回值 ::方法调用 s -> System.out.println(s);
    Consumer<String> consumer = System.out::println;
    consumer.accept("一个入参没有返回值");

    System.out.println("----------------------BiConsumer--------------------");
    // BiConsumer 两个入参没有返回值
    BiConsumer<String, Integer> biConsumer = (s, i) -> System.out.println("接受的参数" + s + i);
    biConsumer.accept("Hello", 1);

    System.out.println("----------------------Supplier--------------------");
    // Supplier 无入参,一个返回值
    Supplier<String> supplier = () -> "无入参,一个返回值";
    String supplierResult = supplier.get();
    System.out.println(supplierResult);

    System.out.println("----------------------Predicate--------------------");
    // Predicate一个入参,返回boolean值
    Predicate<Integer> predicate = integer -> integer > 0;
    boolean test1 = predicate.test(0);
    boolean test2 = predicate.test(1);
    System.out.println(test1);
    System.out.println(test2);

    System.out.println("----------------------BiPredicate--------------------");
    // BiPredicate两个入参,返回boolean值
    BiPredicate<String, String> biPredicate = String::equals;
    boolean biPredicateResult = biPredicate.test("1", "2");
    System.out.println(biPredicateResult);

    System.out.println("----------------------UnaryOperator--------------------");
    // UnaryOperator一个入参,一个返回值,入参类型跟返回类型一致
    UnaryOperator<String> unaryOperator = s -> {
        String hello = "Hello ";
        return hello + s;
    };
    String unaryOperatorResult = unaryOperator.apply("World!");
    System.out.println(unaryOperatorResult);

    System.out.println("----------------------BinaryOperator--------------------");
    // BinaryOperator两个入参,一个返回值,入参类型跟返回类型三个是一致的
    BinaryOperator<String> binaryOperator = (s1, s2) -> s1 + s2;
    String binaryOperatorResult = binaryOperator.apply("第一个入参", "第二个入参");
    System.out.println(binaryOperatorResult);

}

结果

----------------------Function--------------------
Function一个入参一个返回值
----------------------BiFunction--------------------
参数1参数2
----------------------Consumer--------------------
一个入参没有返回值
----------------------BiConsumer--------------------
接受的参数Hello1
----------------------Supplier--------------------
无入参,一个返回值
----------------------Predicate--------------------
false
true
----------------------BiPredicate--------------------
false
----------------------UnaryOperator--------------------
Hello World!
----------------------BinaryOperator--------------------
第一个入参第二个入参

image-20201123095236124

image-20201123095308252

image-20201123095353962

3.2 方法引用

正常的Lambda写法

@Test
public void test3() {
    // 正常的Lambda表达式
    Consumer<String> consumer1 = s -> System.out.println(s);
    consumer1.accept("正常的Lambda表达式");

    // 方法引用写法
    Consumer<String> consumer2 = System.out::println;
    consumer2.accept("方法引用写法");
}

使用方法引用比普通的Lambda表达式又简洁了一些。如果函数式接口的实现恰好可以通过调用一个方法来实现,那么我们可以使用方法引用。

方法引用又分了几种:

  • 静态方法的方法引用。
  • 非静态方法的方法引用。
  • 构造函数的方法引用。
public class FunctionVo {
    /**
     * 静态方法
     */
    public static void staticFunction(String value) {
        System.out.println("静态方法" + value);
    }

    /**
     * 实例方法
     */
    public void instanceFunction(String value) {
        System.out.println("实例方法" + value);
    }

    /**
     * 无参构造方法
     */
    public FunctionVo() {

    }

    /**
     * 有参构造方法
     *
     * @param value
     */
    public FunctionVo(String value) {
        System.out.println(value);
    }
}
@Test
public void test4() {
    // 静态方法引用--通过类名调用
    Consumer<String> staticFunction = FunctionVo::staticFunction;
    staticFunction.accept("What!");

    //实例方法引用--通过实例调用
    FunctionVo functionVo = new FunctionVo();
    Consumer<String> instanceFunction = functionVo::instanceFunction;
    instanceFunction.accept("How!");

    // 构造方法方法引用--无参数
    Supplier<FunctionVo> supplier1 = FunctionVo::new;
    FunctionVo functionVo1 = supplier1.get();
    System.out.println(functionVo1);

    // 构造方法方法引用--有参数
    Consumer<String> consumer = FunctionVo::new;
    consumer.accept("Dog!");
}

结果

静态方法What!
实例方法How!
com.qiang.test.FunctionVo@6073f712
Dog!

四、Date API

Clock 类提供了访问当前日期和时间的方法,Clock 是时区敏感的,可以用来取代 System.currentTimeMillis() 来获取当前的微秒数。某一个特定的时间点也可以使用 Instant 类来表示,Instant 类也可以用来创建旧版本的java.util.Date 对象。

@org.junit.Test
    public void test40() {
    // java8新特性Date API Clock
    Clock clock = Clock.systemDefaultZone();
    // 1609832443466
    System.out.println("当前时间毫秒数:" + System.currentTimeMillis());
    // 1609832443466
    System.out.println("当前时间毫秒数:" + clock.millis());
    // 2021-01-05T07:40:43.466Z
    System.out.println("当前系统时间:" + clock.instant());
    // Tue Jan 05 15:42:33 CST 2021
    System.out.println("当前系统时间:" + Date.from(clock.instant()));
}

在新API中时区使用 ZoneId 来表示。时区可以很方便的使用静态方法of来获取到。 抽象类ZoneId(在java.time包中)表示一个区域标识符。 它有一个名为getAvailableZoneIds的静态方法,它返回所有区域标识符。

@org.junit.Test
    public void test41() {
    // java8新特性Date API ZoneId
    Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
    // [Asia/Aden, America/Cuiaba ...... US/Pacific, Europe/Monaco]
    System.out.println("所有区域标识符" + availableZoneIds);
    // ZoneRules[currentStandardOffset=+01:00]
    System.out.println("获取此标识允许执行的计算的时区规则" + ZoneId.of("Europe/Monaco").getRules());
}