J2SE-Jdk1.8

前言

Jdk1.8新特性:这里只介绍Optional、Lambda表达式、函数式接口、Stream API,其他特性例如Date Api等参考菜鸟文章

Optional

最初对Optional的印象就是解决了空指针的问题,但是看了几篇文章感觉不实用,放弃。

后面学***A的过程中,经常碰到被Optional包装的返回结果,于是尝试着使用。目前最直接的感觉就是代码风格发生了变化。如下:

package com.huobi.examples;

import java.util.Objects;
import java.util.Optional;

/**
 * 测试Optional
 *
 * @author hulei
 * @date 2021/3/31
 */
public class OptionalTest {
    /**
     * 查询name,如果为null抛出异常
     *
     * @param args 参数
     */
    public static void main(String[] args) {
        // 对于第一种查询name方式的处理
        String name = getUserName().orElseThrow(RuntimeException::new);
        System.out.println(name);

        // 对于第二种查询name方式的处理
        String name2 = getUserName2();
        if (Objects.isNull(name2)) {
            throw new RuntimeException();
        }
        System.out.println(name2);
    }

    /**
     * 第一种查询name的方式, 将可能为null的结果用Optional进行包装后返回
     *
     * @return name的包装
     */
    public static Optional<String> getUserName() {
        // 假定name通过数据库查询,可能为null
        String name = "";
        return Optional.ofNullable(name);
    }

    /**
     * 第二种查询name的方式, 将可能为null的结果直接返回
     *
     * @return name
     */
    public static String getUserName2() {
        String name = "";
        return name;
    }
}

通过上述演示,对于方法可能返回null的情况下,通过Optional包装后

  1. 调用方会很容易知道此方法可能返回null,则会对返回结果进行一定的处理;
  2. 另外一方面使用Optional处理null的链式代码风格理论上更加简洁。

Optional中常用方法演示如下,更多Optional方法的使用参考CSDN上文章

package com.huobi.examples;

import java.util.Optional;

/**
 * 测试Optional
 *
 * @author hulei
 * @date 2021/3/31
 */
public class OptionalTest2 {
    public static void main(String[] args) {
        // 包装非null的对象,如果被包装的对象是null,此方法会返回空指针异常
        Optional<String> o1 = Optional.of("xxx");

        // 包装可能为null的对象,被包装的对象是否为null都不会抛出异常
        Optional<String> o = Optional.ofNullable(null);

        // 用于测试Optional是否包装了null, 另外一种测试方式是isPresent()
        Optional<Object> empty = Optional.empty();
        System.out.println(empty.equals(o));
        System.out.println(o.isPresent());

        // 如果被包装的对象为null,则返回特定对象
        String s = o.orElse("");

        // 如果被包装对象为null,则通过一段代码(lambda表达式),返回特定对象
        String xxx = o.orElseGet(() -> {
            System.out.println("xxx");
            return "";
        });

        // 如果被包装对象为null,则抛出异常
        String s1 = o.orElseThrow(RuntimeException::new);
    }
}

函数式接口

函数式接口是指仅用一个抽象方法的接口(Interface),但允许存在多个非抽象方法(Jdk1.8中允许在接口中定义非抽象方法,default方法、static方法,具体参考CSDN文章)。

按上述定义,只要接口中抽象方法仅有一个即为函数是接口,无论接口是否被@FunctionalInterface注解修饰。并且Java中很多现存接口都被纳入函数是接口的范畴:Runable、Callable等,具体参考菜鸟文章

实现函数式接口:按照实现普通接口的方式,即通过implements实现接口;通过new匿名类实现接口;JDK1.8中引入新的实现接口的方式,lambda表达式。

package com.huobi.hl;

import java.util.function.Consumer;
import java.util.stream.Stream;

/**
 * 函数式接口测试
 *
 * @author hulei
 * @date 2021/4/1
 */
public class FunctionTest {
    public static void main(String[] args) {
        // 通过new 匿名函数实现函数式接口
        Stream.of("1", "2").forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });

        // 通过lambda表达式实现函数式接口
        Stream.of("1", "2").forEach(System.out::println);
    }
    
    /**
     * 通过implements实现函数式接口
     *
     * @author hulei
     * @version 2021/4/1 9:45 
     */
    public static class MyConsumer implements Consumer<String> {
        @Override
        public void accept(String s) {
            System.out.println(s);
        }
    }
}

Jdk1.8中新增的函数式接口参考菜鸟文章,以下仅介绍常用的几个:

函数式接口 方法 入参 返回 理解
Predicate boolean test(T t) T boolean 断言:即传入t,按照一定条件判断并返回
Consumer void accept(T t) T 不返回 消费:即传入t,消费t,并且不返回
Supplier T get(); 不传入 返回T 供应:即不传入,但返回值
Function<T, R> R apply(T t); 传入T 返回R 应用:即传入t,消费t并按规则转换后返回
package com.huobi.hl;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 函数式接口测试
 *
 * @author hulei
 * @date 2021/4/1
 */
public class FunctionTest2 {
    public static <T> void main(String[] args) {
        List<String> list = Stream.of("张三", "李四", "王二麻").collect(Collectors.toList());

        // 使用predicate-test,判断list中是否存在长度大于2的元素
        boolean match = list.stream().anyMatch(s -> s.length() >= 2);
        System.out.println(match);

        // 使用function-apply,获取姓名中第一个字
        Object[] array = list.stream().map(s -> s.substring(0, 1)).toArray();
        System.out.println(Arrays.toString(array));

        // 使用consumer-accept,打印数组
        list.forEach((s) -> {
            System.out.print(s);
            System.out.print(" ");
        });
        System.out.println();

        // 使用supplier-get,如果user为null,通过supplier的实现提供user返回
        String user = null;
        String s = Optional.ofNullable(user).orElseGet(() -> "default".substring(0, 1));
        System.out.println(s);
    }
}

序列化分析

通过方法引用获取字段名称,下述代码摘自tk-mybatis

package tk.mybatis.mapper.weekend.reflection;

import tk.mybatis.mapper.weekend.Fn;

import java.beans.Introspector;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.regex.Pattern;

/**
 * @author Frank
 */
public class Reflections {
    private static final Pattern GET_PATTERN = Pattern.compile("^get[A-Z].*");
    private static final Pattern IS_PATTERN  = Pattern.compile("^is[A-Z].*");

    private Reflections() {
    }

    public static String fnToFieldName(Fn fn) {
        try {
            Method method = fn.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(Boolean.TRUE);
            SerializedLambda serializedLambda = (SerializedLambda) method.invoke(fn);
            String getter = serializedLambda.getImplMethodName();
            if (GET_PATTERN.matcher(getter).matches()) {
                getter = getter.substring(3);
            } else if (IS_PATTERN.matcher(getter).matches()) {
                getter = getter.substring(2);
            }
            return Introspector.decapitalize(getter);
        } catch (ReflectiveOperationException e) {
            throw new ReflectionOperationException(e);
        }
    }
}

package tk.mybatis.mapper.weekend;

import java.io.Serializable;
import java.util.function.Function;

/**
 * @author Frank
 */
public interface Fn<T, R> extends Function<T, R>, Serializable {
}

编译器捕捉到可序列化的Lambda表达式,Lambda元工厂必须返回一个实现了writeReplace方法的对象;writeReplace方法需要返回一个实现readResolve方法的SerializedLambda的实现类。编译器翻译Lambda表达式的原理参考Lambda表示式深入理解

Lambda表达式

如函数式接口一节中所述,Lambda表达式专门用于实现函数是接口,用于取代匿名实现的复杂写法。

基本语法:(参数) -> {方法体}, 具体参考菜鸟文章

方法引用

方法引用是一种简化的lambda表达式,使用::引用类中已经实现的方法或者构造方法,方法引用有四种类型,具体参考博客园文章,使用时需要对应特定的函数式接口。

package com.huobi.hl;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 函数式接口测试
 *
 * @author hulei
 * @date 2021/4/1
 */
public class FunctionTest3 {
    private String prop;

    public String getProp() {
        return prop;
    }

    public static <T> void main(String[] args) {
        List<Integer> list = Stream.of(1, 2, 3).collect(Collectors.toList());
        List<Integer> sub = Stream.of(1).collect(Collectors.toList());

        // 通过实例引用非静态方法: predicate - test
        boolean b = list.stream().anyMatch(sub::contains);

        // 通过类引用非静态方法: function - apply, 通过类引用非静态方法、通过类引用静态方法,对应的接口函数不同
        Function<FunctionTest3, String> prop = FunctionTest3::getProp;
        List<String> props = new ArrayList<FunctionTest3>().stream().map(prop).collect(Collectors.toList());

        // 通过类引用静态方法: consumer - accept
        Stream.of("1").forEach(FunctionTest3::print);

        // 引用构造方法:supplier - get
        Object o = null;
        Optional.ofNullable(o).orElseGet(Object::new);
    }

    public static void print(String s) {
        System.out.println(s);
    }
}

Stream API

流式API,可以用于处理集合、数组、IO等,让数据流经过管道中不同的处理方法达到最终的要求。这里介绍Steam API对集合的常用操作,Stream适合处理数据量不大的集合,对处理效率影响小,代码比较简洁

package com.huobi.hl;

import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 流式api测试
 *
 * @author hulei
 * @date 2021/4/1
 */
public class StreamTest {
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class User {
        private String id;
        private String name;
        private Integer age;
        private String sex;
    }

    public static void main(String[] args) {
        // 初始化数据
        User zs = new User.UserBuilder().id("1").name("张三").age(18).sex("1").build();
        User ls = new User.UserBuilder().id("2").name("李四").age(20).sex("1").build();
        User xh = new User.UserBuilder().id("3").name("小红").age(22).sex("0").build();

        // 构造集合
        List<User> list = Stream.of(zs, ls, xh).collect(Collectors.toList());

        // 转换对象集合: ["张三","李四","小红"]
        List<String> nameList = list.stream().map(user -> user.name).collect(Collectors.toList());
        System.out.println(JSON.toJSON(nameList));

        // 转换为map: {"1":"张三","2":"李四","3":"小红"}
        Map<String, String> collect1 = list.stream().collect(Collectors.toMap(User::getId, User::getName));
        System.out.println(JSON.toJSON(collect1));

        // 分组: {"0":[{"sex":"0","name":"小红","id":"3","age":22}],"1":[{"sex":"1","name":"张三","id":"1","age":18},{"sex":"1","name":"李四","id":"2","age":20}]}
        Map<String, List<User>> collect2 = list.stream().collect(Collectors.groupingBy(User::getSex));
        System.out.println(JSON.toJSON(collect2));

        // 分组,并且转换组中对象: {"0":["小红"],"1":["张三","李四"]}
        Map<String, List<String>> collect3 = list.stream().collect(Collectors.groupingBy(User::getSex, Collectors.mapping(User::getName, Collectors.toList())));
        System.out.println(JSON.toJSON(collect3));

        // 分组计数: {"0":1,"1":2}
        Map<String, Long> collect = list.stream().collect(Collectors.groupingBy(User::getSex, Collectors.counting()));
        System.out.println(JSON.toJSON(collect));

        // 过滤: ["李四","小红"]
        List<String> collect4 = list.stream().filter(user -> user.getAge() >= 20).map(User::getName).collect(Collectors.toList());
        System.out.println(JSON.toJSON(collect4));
        
        // 找出不满足指定集合条件的元素(双重循环): [李四, 小红]
        List<String> exclude = Stream.of("张", "王").collect(Collectors.toList());
        List<String> collect5 = list.stream().filter(user -> exclude.stream().noneMatch(s -> user.getName().startsWith(s))).map(User::getName).collect(Collectors.toList());
        System.out.println(collect5);
    }
}
posted @ 2021-04-02 10:22  规划中~~~  阅读(90)  评论(0)    收藏  举报