JDK8新特性

一. lambda表达式

  1. 在JDK8之前在将一个接口作为形参时需要使用匿名内部类的方法重写抽象方法,
    这种方法比较麻烦,因为我们只关心方法体内部的代码,不关心参数、方法名灯,因此使用lambda表达式代替匿名内部类的方法作为形参。
  2. lambda使用,是可以通过上下文推断出参数
  3. 使用@FunctionalInterface校验一个接口是否是函数式接口
  4. 匿名内部类和lambda作为形参的区别
  5. 形参类型不一样,匿名内部类作为形参可以是类、抽象类、接口,lambda只能是接口
  6. 抽象方法数量不一样,匿名内部类可以是任意个抽象方法,lambda只能是一个匿名方法
  7. 实现原理不一样,匿名内部类是编译后形成class文件,lambda是运行时动态生成class
java 复制代码
/**
 * @Author 
 * @Date 2024/4/16 19:47
 */
@FunctionalInterface
public interface TestService {
    void test();
}

/**
 * @Author 
 * @Date 2024/4/16 19:48
 */
public class Test01 {

    @Test
    public void test01(){
        //匿名内部类方式
        TestService testService = new TestService() {
            @Override
            public void test() {
                System.out.println("这是匿名内部类");
            }
        };
        show(testService);

        //lambda方式
        TestService testService1 =() -> System.out.println("这是lombda方式");
        show(testService1);
    }

    public static void show(TestService testService){
        testService.test();
    }
}

二. 接口增强

1. 接口中新增默认方法

  1. JDK8之前接口中只能有抽象方法和静态变量.
  2. 使用默认方法后可以使接口更加灵活,比如一个接口有多个实现类,那么在接口中新增一个抽象方法,那么它的实现类中必须强制重写该抽象方法,而使用默认方法后,它的实现类不需要强制重写抽象方法,可以重写,重写后覆盖默认方法,不重写,继承默认方法。

2. 接口中新增静态方法

静态方法可以通过接口名来调用

3. 默认方法和静态方法的区别

  1. 默认方法可以继承也可以重写,可以通过实例调用.
  2. 静态方法不能被重写,也不能被继承,只能通过接口名来调用,不能通过实例调用.
java 复制代码
public class Test01 {
    @Test
    public void test() {
        B b = new B();
        b.test01();
        b.test02();

        C c = new C();
        c.test01();
        c.test02();

        A.test03();
    }

    interface A {
        void test01();

        default void test02() {
            System.out.println("A接口的默认实现");
        }

        static void test03() {
            System.out.println("接口中的静态方法");
        }
    }

    class B implements A {
        @Override
        public void test01() {
            System.out.println("B实现接口A");
        }

        @Override
        public void test02() {
            System.out.println("B重写A中的默认实现方法");
        }
    }

    class C implements A {
        @Override
        public void test01() {
            System.out.println("C实现接口A");
        }

    }
}
text 复制代码
B实现接口A
B重写A中的默认实现方法
C实现接口A
A接口的默认实现
接口中的静态方法

三. 函数式接口(四个默认)

1. 四个函数式接口

  1. Function<T,R>:函数型接口
  2. Consumer:消费型接口
  3. Superior:提供型接口
  4. Predicate:判断型接口

2.提供函数式接口的原因

在使用lambda表达式的时候,需要提供一个函数式接口,这就导致每次使用lambda表达式 都要定义一个函数式接口,但是不需要去关心函数的名称,只需要关心参数和返回类型,因此JDK8提供了四个函数式接口,其中参数和返回值使用泛型 这样使用lambda时候不需要定义函数式接口,拿来即用,使用泛型定义返回值和参数,使用起来更加灵活。

java 复制代码
/**
 * @Author
 * @Date 2024/4/17 21:22
 */
public class Test01 {

    /**
     * Function<T,R> 函数型接口
     * 传入T类型参数
     * 返回R类型对象
     */
    @Test
    public void test01(){
        Integer integer = t1("hello",msg -> msg.length());
        System.out.println(integer);
    }
    // 求一个字符串的长度
    public Integer t1(String str,Function<String, Integer> fun){
        return fun.apply(str);
    }

    //Function两个默认方法
    //Function<T, R> andThen(Function<? super R,? extends V> after): 返回一个组合函数,该函数结果应用到after函数中
    //Function<T, R> compose(Function<? super V,? extends T> before): 返回一个组合函数,首先将入参应用到before函数,再将before函数结果应用到该函数中
    @Test
    public void test011(){
        Function<String,String> f1 = str -> str + " Tom";
        Function<String,String> f2 = str -> str + " !";
        String str = f1.andThen(f2).apply("hello");
        String str2 = f2.compose(f1).apply("hello");
        System.out.println(str);
        System.out.println(str2);
    }

    /**
     * Supplier<T> 共计型接口
     * 返回一个T类型的对象
     */
    @Test
    public void test02(){
        String s = t2(() -> "hello");
        System.out.println(s);
    }
    // 返回一个字符串
    public String t2(Supplier<String> sup){
        return sup.get();
    }

    /**
     * Consumer<T> 消费型接口
     * 传入一个T类型的参数
     */
    @Test
    public void test03(){
        t3(1234556,msg -> System.out.println(msg.toString()));
    }
    //将一个数字转换为字符串并打印
    public void t3(Integer num,Consumer<Integer> con){
         con.accept(num);
    }
    //Consumer默认函数
    //default Consumer andThen(Consumer<? super T> after): 返回一个组合函数,after将会在该函数执行之后应用
    @Test
    public void test033(){
        StringBuilder sb = new StringBuilder("hello");
        Consumer<StringBuilder> c1 = str -> str.append(" Tom");
        Consumer<StringBuilder> c2 = str -> str.append(" !");
        c1.andThen(c2).accept(sb);
        System.out.println(sb);
    }
    /**
     * Predicate<T> 断定型接口
     * 传入T类型的参数
     * 返回boolean类型
     */
    @Test
    public void test04(){
        boolean b = t4("hello",msg -> msg.length() > 3);
        System.out.println(b);
    }
    //判断一个字符串的长度是否大于3
    public boolean t4(String str,Predicate<String> pre){
       return pre.test(str);
    }
}

四. 方法引用

1.什么是方法引用

  1. 由于在使用lambda表达式时候只关心lambda表达式中的代码逻辑,如果代码中有现成的方法和lambda表达式中代码逻辑一样 ,那么就可以使用现有的方法,而不需要再次编写逻辑,这就是方法引用,方法引用是为了避免代码冗余,是开发更简单。
  2. 但使用方法引用需要lambda表达式符合特定情况下的一种缩写方式
  3. 被引用的方法满足的特定条件为:
    a. 返回值和lambda表达式相同
    b.参数和lambda表达式相同

2.引用方式

  1. 对象名::方法名
  2. 类名::静态方法
  3. 类名::引用实例方法
  4. 类名::构造器
  5. 数组::构造器
java 复制代码
/**
 * @Author 
 * @Date 2024/4/17 22:34
 */
public class Test01 {

    /**
     * 对象::方法名
     */
    @Test
    public void test01(){
        Date date = new Date();
        Supplier<Long> supplier1 = () -> date.getTime();
        System.out.println(supplier1.get());
        Supplier<Long> supplier2 = date::getTime;
        System.out.println(supplier2.get());
    }

    /**
     * 类名::静态方法
     */
    @Test
    public void test02(){
        Supplier<Long> supplier1 = () ->System.currentTimeMillis();
        System.out.println(supplier1.get());
        Supplier<Long> supplier2 = System::currentTimeMillis;
        System.out.println(supplier2.get());

    }

    /**
     * 类名::引用实例方法
     */
    @Test
    public void test03(){
        Function<String,Integer> f1 = str ->str.length();
        System.out.println(f1.apply("qwer"));
        Function<String,Integer> f2 = String::length;
        System.out.println(f2.apply("abc"));
    }

    /**
     * 类名::构造器
     */
    @Test
    public void test04(){
        Supplier<Date> supplier1 = () -> new Date();
        System.out.println(supplier1.get());
        Supplier<Date> supplier2 = Date::new;
        System.out.println(supplier2.get());
    }

    /**
     * 数组::构造器
     */
    @Test
    public void test05(){
        Function<Integer,String[]> f1 = length -> new String[length];
        System.out.println(f1.apply(2).length);
        Function<Integer,String[]> f2 = String[]::new;
        System.out.println(f2.apply(3).length);

    }
}

五. StreamAPI

1. 新增StreamAPI的原因

在JDK8之前操作集合的时候需要for循环去遍历集合,并且需要新的对象来接收处理后的集合,操作比较麻烦,因此,JDK8新增StreamAPI来做一些集合操作.

2. 什么是Stream

1.Stream关注的是对数据的计算,与CPU打交道,集合关注的是数据的存储,与内存打交道

2.Stream自己不会存储数据.Stream不改变原对象,相反,它会返回一个持有结果的新的Stream,Stream是延迟操作的,需要结果的时候才会执行(惰性加载)

3.Stream的执行流程:Stream实例化 -> 一系列的中间操作(过滤、映射等) -> 终止操作

4.说明:Stream一个中间操作链对数据源数据进行处理,一旦执行终止操作,就执行中间操作链,并产生结果,之后便不再被使用.

3.StreamAPI大概分为三部分

3.1常用API

java 复制代码
/**
* @Author
* @Date 2024/4/21 0:43
* stream常用api
*/
public class TestStreamApi {

   @Test
   public void Test01(){
       //1. forEach:遍历
       System.out.println("forEach:");
       Arrays.asList("a", "b", "c").stream().forEach(System.out::println);

       //2. filter:过滤
       System.out.println("filter:");
       Arrays.asList("a", "b", "c").stream().filter(item -> item.equals("a")).collect(Collectors.toList()).forEach(System.out::println);

       //3. map:映射
       System.out.println("map:");
       Arrays.asList("a", "b", "c").stream().map(item -> item+"$").forEach(System.out::println);

       //4. sorted:排序
       System.out.println("sorted:");
       Arrays.asList("v", "b", "c").stream().sorted().forEach(System.out::println);
       //可以自定义比较器
       Arrays.asList(1, 3, 4).stream().sorted((a,b) -> b-a).forEach(System.out::println);

       //5. limit: 截取
       Arrays.asList("v", "b", "c").stream().limit(2).forEach(System.out::println);

       //6. skip: 跳过前n个元素
       Arrays.asList("v", "b", "c").stream().skip(2).forEach(System.out::println);

       //7. distinct: 去重
       Arrays.asList("v", "b", "c","v").stream().distinct().forEach(System.out::println);

       //8. match: 匹配
       boolean b1 = Arrays.asList("v", "b", "c", "v").stream().allMatch(item -> item.endsWith("b"));
       System.out.println("是否都以b字母开头" + b1);
       boolean b2 = Arrays.asList("v", "b", "c", "v").stream().anyMatch(item -> item.endsWith("b"));
       System.out.println("是否存在以b字母开头" + b2);
       boolean b3 = Arrays.asList("v", "b", "c", "v").stream().noneMatch(item -> item.endsWith("b"));
       System.out.println("是否都不以b字母开头" + b3);

       //9. find: findAny 返回任意一个元素 findFirst返回第一个元素
       Optional<String> any = Arrays.asList("v", "b", "c","b").stream().findAny();
       System.out.println(any.get());
       Optional<String> first = Arrays.asList("v", "b", "c","b").stream().findFirst();
       System.out.println(first.get());

       //10. max\min: 最大值最小值,可自定义比较器
       Optional<Integer> max = Arrays.asList(1, 2, 3, 4, 5, 6, 7).stream().max(Integer::compareTo);
       System.out.println("max:" + max.get());
       Optional<Integer> min = Arrays.asList(1, 2, 3, 4, 5, 6, 7).stream().min(Integer::compareTo);
       System.out.println("min:" + min.get());

       //11. reduce:归并操作
       String reduce1 = Arrays.asList("v", "b", "c", "v").stream().reduce("", (a, b) -> a + b);
       System.out.println("拼接字符串" + reduce1);
       Integer reduce2 = Arrays.asList(1, 2, 3, 4).stream().reduce(0, (a, b) -> Integer.sum(a, b));
       System.out.println("求和" +reduce2);

       //12. peek: 用于调试程序使用,可在流执行中进行操作
       List<Integer> collect = Arrays.asList(1, 2, 3, 4, 5, 6, 7).stream().filter(item -> item > 3)
               .peek(a -> System.out.println("第一次打印")).filter(item -> item > 4)
               .peek(a -> System.out.println("第二次打印")).collect(Collectors.toList());


       //13. mapToXXX:映射为响应数据类型
       Arrays.asList("v", "b", "c","v").stream().mapToDouble(a -> a.length()).forEach(System.out::println);
       Arrays.asList("v", "b", "c","v").stream().mapToLong(a -> a.length()).forEach(System.out::println);
       Arrays.asList("v", "b", "c","v").stream().mapToInt(a -> a.length()).forEach(System.out::println);

       //14, flatMapToXXX: 将一个流中的元素转换为一个XXXStream并合并为一个流
       System.out.println("flatMapToXXX-----");
       List<Student> students = new ArrayList<>();
       Student student1 = new Student("张三");
       List<Score> scores1 = new ArrayList<>();
       scores1.add(new Score("语文",100));
       scores1.add(new Score("数学",100));
       scores1.add(new Score("英语",100));
       student1.setScores(scores1);
       Student student2 = new Student("张三");
       List<Score> scores2 = new ArrayList<>();
       scores2.add(new Score("语文",99));
       scores2.add(new Score("数学",99));
       scores2.add(new Score("英语",99));
       student2.setScores(scores2);
       Student student3 = new Student("王五");
       List<Score> scores3 = new ArrayList<>();
       scores3.add(new Score("语文",98));
       scores3.add(new Score("数学",98));
       scores3.add(new Score("英语",98));
       student3.setScores(scores3);
       students.add(student1);
       students.add(student2);
       students.add(student3);
       System.out.println(students);
       //计算所有学生所有学科成绩总和
       int sum = students.stream().flatMapToInt(item -> item.getScores().stream().mapToInt(Score::getSum)).sum();
       System.out.println(sum);
       //计算每个学生的总和并转为map,key冲突,报错
       Map<String, Integer> collect1 = students.stream().
               collect(Collectors.toMap(Student::getName, item -> item.getScores().stream().map(Score::getSum).reduce(0,(a,b) -> a+b))
               );
       //解决key冲突的问题 选择第二个key的值value2覆盖第一个key的值value1
       Map<String, Integer> collect2 = students.stream().
               collect(Collectors.toMap(Student::getName, item -> item.getScores().stream().map(Score::getSum).reduce(0, (a, b) -> a + b),(a,b) -> a)
               );
       System.out.println(collect2);


   }

3.2 收集器

java 复制代码
/**
 * @Author 
 * @Date 2024/4/21 14:58
 * 收集数据
 */
public class TestCollect {

    /**
     * 1.收集为set或者list或具体实现类
     */
    @Test
    public void test01(){
        //收集为list
        List<String> list = Stream.of("a", "b", "c").collect(Collectors.toList());
        System.out.println(list);
        //收集为set
        Set<String> set = Stream.of("a", "b", "c","a").collect(Collectors.toSet());
        System.out.println(set);
        //收集为具体集合的实现
        ArrayList<String> arrayList = Stream.of("a", "b", "c", "a").collect(Collectors.toCollection(ArrayList::new));
        System.out.println(arrayList);
        HashSet<String> hashSet = Stream.of("a", "b", "c", "a").collect(Collectors.toCollection(HashSet::new));
        System.out.println(hashSet);
    }

    /**
     * 2. 收集为数组
     */
    @Test
    public void test02(){
        //默认收集为Object数组
        Object[] objects = Stream.of("a", "b", "c").toArray();
        for (int i = 0; i < objects.length; i++) {
            System.out.println(objects[i]);
        }
        //可指定类型
        String[] strings = Stream.of("a", "b", "c").toArray(String[]::new);
        for (int i = 0; i < strings.length; i++) {
            System.out.println(strings[i]);
        }
        Integer[] integers = Stream.of("acc", "b", "c").map(String::length).toArray(Integer[]::new);
        for (int i = 0; i < integers.length; i++) {
            System.out.println(integers[i]);
        }
    }

    /**
     * 3. 聚合运算
     */
    @Test
    public void test03(){
        List<Person> people = Arrays.asList(
                new Person("张三", 18),
                new Person("李四", 23),
                new Person("王五", 33)
        );

        //最大值
        Optional<Person> max = people.stream().collect(Collectors.maxBy((a, b) -> a.getAge() - b.getAge()));
        System.out.println("年龄最大的为:"+max.get());

        //最小值
        Optional<Person> min = people.stream().collect(Collectors.minBy((a, b) -> a.getAge() - b.getAge()));
        System.out.println("年龄最小的为:"+min.get());

        //求和
        Integer sum = people.stream().collect(Collectors.summingInt(Person::getAge));
        System.out.println("年龄总和为:" + sum);

        //平均值
        Double avg = people.stream().collect(Collectors.averagingInt(Person::getAge));
        System.out.println("年龄平均值为:" + avg);

        //统计数量
        Long count = people.stream().filter(item -> item.getAge() > 20).collect(Collectors.counting());
        System.out.println("年龄大于20总人数为:" + count);

    }

    /**
     * 4. 分组函数
     */
    @Test
    public void test04(){
        List<Person> people = Arrays.asList(
                new Person("张三", 16),
                new Person("李四", 23),
                new Person("王五", 33),
                new Person("赵六", 33)
        );
        //按姓名分组
        Map<String, List<Person>> map1 = people.stream().collect(Collectors.groupingBy(Person::getName));
        System.out.println("按姓名分组结果" + map1);
        //按年龄分组
        Map<String, List<Person>> map2 = people.stream().collect(Collectors.groupingBy(item -> item.getAge() >= 18 ? "成年" : "未成年"));
        System.out.println("按年龄分组结果" + map2);
    }

    /**
     * 5. 分组函数-多级分组
     */
    @Test
    public void test05(){
        List<Person> people = Arrays.asList(
                new Person("张三", 16,180),
                new Person("李四", 23,188),
                new Person("王五", 33,170),
                new Person("赵六", 33,180),
                new Person("赵六", 33,180)
        );
        //先按age分组,再按height分组
        Map<Integer, Map<Integer, List<Person>>> collect = people.stream().collect(Collectors.groupingBy(Person::getAge, Collectors.groupingBy(Person::getHeight)));
        System.out.println(collect);
    }

    /**
     * 6. 分区操作
     */
    @Test
    public void test06(){
        List<Person> people = Arrays.asList(
                new Person("张三", 16,180),
                new Person("李四", 23,188),
                new Person("王五", 33,170),
                new Person("赵六", 33,180),
                new Person("赵六", 33,180)
        );
        Map<Boolean, List<Person>> collect = people.stream().collect(Collectors.partitioningBy(item -> item.getAge() > 18));
        System.out.println(collect);
    }

    /**
     * 7. 拼接操作
     */
    @Test
    public void test07(){
        List<Person> people = Arrays.asList(
                new Person("张三", 16,180),
                new Person("李四", 23,188),
                new Person("王五", 33,170),
                new Person("赵六", 33,180),
                new Person("赵六", 33,180)
        );
        String collect = people.stream().map(Person::getName).collect(Collectors.joining("-", "前缀", "后缀"));
        System.out.println(collect);
    }
}

3.3 并行流

java 复制代码
/**
 * @Author
 * @Date 2024/4/20 23:41
 * 并行流
 */
public class TestParallelFlow {

    /**
     * 1.生成并行流的方式
     */
    @Test
    public void test01(){
        //1.使用Stream的parallel()方法
        Stream.of("a","b","c").parallel().forEach(res -> System.out.println(Thread.currentThread().getName() + "-" +res));
        //2.使用Collection中的parallelStream()方法
        Arrays.asList("a","b","c").parallelStream();
    }

    /**
     * 2.并行流、串行流的对比(只有数据量到达一定级别,并行处理操作耗时比较长的情况下才使用并行流)
     */

    private static final Long count = 1000000000L;
    @Test
    public void test02(){
        //串行操作 1204
        long before1 = System.currentTimeMillis();
        LongStream.range(0, count).reduce(0, Long::sum);
        long after1 = System.currentTimeMillis();
        long l1 = after1 - before1;
        System.out.println("串行处理时间为:" + l1);
        //并行处理了时间 252
        long before2 = System.currentTimeMillis();
        LongStream.range(0, count).parallel().reduce(0, Long::sum);
        long after2 = System.currentTimeMillis();
        long l2 = after2 - before2;
        System.out.println("并行处理时间为:" + l2);
    }

    /**
     * 3.并行流的安全问题
     */
    @Test
    public void test03(){
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list.add(i);
        }

        //不使用并行流,数组长度为1000
        List<Integer> newList1 = new ArrayList<>();
        list.stream().forEach(newList1::add);
        System.out.println(newList1.size());

        //使用并行流后,集合长度不一定为1000,要么小于1000,要么数组越界,有并发问题
        List<Integer> newList2 = new ArrayList<>();
        list.stream().parallel().forEach(newList2::add);
        System.out.println(newList2.size());
    }

    /**
     * 4.解决并行流程的安全问题
     */
    @Test
    public void test04(){
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list.add(i);
        }

        //1.使用线程安全的容器
        ArrayList<Integer> newList1 = new ArrayList<>();
        //将集合转换为线程安全的
        List<Integer> synchronizedNewList1 = Collections.synchronizedList(newList1);
        //或使用线程安全的容器
        Vector<Integer> vectorNewList1 = new Vector<>();
        list.stream().parallel().forEach(vectorNewList1::add);
        System.out.println("将不安全的集合转为安全集合:" + vectorNewList1.size());

        //2.使用锁
        List<Integer> newList2 = new ArrayList<>();
        ReentrantLock lock = new ReentrantLock();
        IntStream.range(0,1000).parallel().forEach(item -> {
            lock.lock();
            newList2.add(new Integer(item));
            lock.unlock();
        });
        System.out.println("使用锁:" + vectorNewList1.size());

        //3.使用Stream中线程安全的api
        Object[] toArrayList = IntStream.range(0, 1000).parallel().boxed().toArray();
        System.out.println("使用Stream中线程安全的api--toArray():" + toArrayList.length);
        List<Integer> collectList = IntStream.range(0, 1000).parallel().boxed().collect(Collectors.toList());
        System.out.println("使用Stream中线程安全的api--collect():" + collectList.size());

    }
}

六. 新增时间日期API

新增的类

  1. 新增LocalDate、LocalTime,LocalDateTime类,可实现日期的比较、计算,最关键的是解决了多线程下日期操作问题,每次都产生一个新的对象;
  2. 新增Instant日期时间戳类
  3. 新增TemporalAdjuster时间矫正器,可自定义日期矫正器
  4. 新增ZonedDateTime国际化时间
java 复制代码
public class Test01 {
    /**
     * 1. 日期、时间、日期时间的创建
     */
   @Test
    public void test01(){
       LocalDate localDate = LocalDate.of(2024, 4, 13);
       LocalTime localTime = LocalTime.of(1, 12, 12, 222);
       LocalDateTime localDateTime = LocalDateTime.of(2022, 2, 2, 23, 23, 2, 222);
   }

    /**
     * 2. 日期修改和比较
     * withXXX()修改年
     * isXXX()比较日期
     *
     */
   @Test
    public void test02(){
       LocalDate now = LocalDate.now();
       LocalDate localDate = LocalDate.of(2024, 4, 14);
       System.out.println(localDate.isAfter(now));
       System.out.println(localDate.withYear(2023));
       LocalTime localTime = LocalTime.of(1, 12, 12, 222);
       System.out.println(localTime.withHour(3));

   }

    /**
     * 3. 日期解析和格式化 DateTimeFormatter
     */
   @Test
   public void test03(){
       LocalDateTime now = LocalDateTime.now();
       //格式化
       DateTimeFormatter dft = DateTimeFormatter.ISO_LOCAL_DATE;
       System.out.println(now.format(dft));
       //解析
       DateTimeFormatter dft2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
       System.out.println(LocalDateTime.parse("2024-04-13 22:00:00", dft2));
   }

    /**
     * 4. 时间戳类 Instant
     */
   @Test
   public void test04() throws InterruptedException {
       Instant now = Instant.now();
       Thread.sleep(5000);
       Instant now1 =  Instant.now();
       System.out.println(now1.getEpochSecond()- now.getEpochSecond());
   }

    /**
     * 5. 计算日期、日期时间的差
     */
   @Test
   public void test05(){
       //Duration相差多少秒多少纳秒
       LocalTime localTimeNow = LocalTime.now();
       LocalTime localTime = LocalTime.of(16, 31, 0, 0);
       Duration between1 = Duration.between(localTimeNow,localTime);
       System.out.println("相差"  + between1.getSeconds() +"秒," + between1.getNano() +"纳秒");

       //Period计算相差多少年/月/天
       LocalDate localDate = LocalDate.of(2024, 4, 1);
       LocalDate localDateNow = LocalDate.now();
       Period between = Period.between(localDate, localDateNow);
       System.out.println("相差" + between.getYears() + "年," +between.getMonths() + "月," + between.getDays() + "天");

       LocalDateTime localDateTimeNow = LocalDateTime.now();
       LocalDateTime lo = LocalDateTime.of(localDate, localTimeNow);
       Duration between2 = Duration.between(lo, localDateTimeNow);
       System.out.println(between2.getSeconds());
   }

    /**
     * 6. 时间矫正器 TemporalAdjuster
     * 函数式接口,传入一个function,将日期处理后的结果返回
     * TemporalAdjusters
     * 获取特定的日期
     */
    @Test
   public void test06(){
       LocalDateTime now = LocalDateTime.now();
       TemporalAdjuster temporalAdjuster = temporal -> {
           LocalDateTime localDateTime = (LocalDateTime) temporal;
           return localDateTime.withYear(2020);
       };
       System.out.println(now.with(temporalAdjuster));
       System.out.println(now.with(TemporalAdjusters.firstDayOfMonth()));//当月第一天
    }

    /**
     * 7. 时区
     * 带时区的时间
     */
    @Test
    public void test07(){
        System.out.println(ZoneId.getAvailableZoneIds());//获取所有时区

        System.out.println(ZonedDateTime.now());//东八区时间

        System.out.println(ZonedDateTime.now(Clock.systemUTC()));//标准时间

        System.out.println(ZonedDateTime.now(ZoneId.of("America/Marigot")));//美国时间

    }
}

七.Optional类

解决空指针问题

常用api

java 复制代码
public class Test01 {

    /**
     * 1. 创建Optional的方式
     */
    @Test
    public void test01(){
        //1.静态方法Optional.of()方法,但不支持null创建
        Optional<String> opt1 = Optional.of("aaa");
        //NullPointerException
        //Optional<Object> opt2 = Optional.of(null);
        //2.静态方法Optional.ofNullable(),至此null创建
        Optional<Object> opt3 = Optional.ofNullable(null);
        Optional<Object> opt4 = Optional.ofNullable("hello");
    }

    /**
     * 2. 常用api
     */
    @Test
    public void test02(){
        Optional<String> opt1 = Optional.ofNullable("hello");
        //1.get():获取值
        String s = opt1.get();
        System.out.println(s);
        //2.orElse():如果为空,返回一个指定值
        Optional<String> opt2 = Optional.ofNullable(null);
        String test = opt2.orElse("test");
        System.out.println(test);
        //3.orElseGet():如果为空,则提供一个值,传入供给型函数
        Optional<String> opt3 = Optional.ofNullable(null);
        String o = opt3.orElseGet(Test01::t);
        System.out.println(o);
        //4.orElseThrow():如果为空,则抛出一个自定义异常
        Optional<String> opt4 = Optional.ofNullable(null);
        opt4.orElseThrow(() -> new RuntimeException("值为空"));
        //5.isPresent():判断Optional对象是否不为空
        Optional<String> opt5 = Optional.ofNullable("11");
        boolean present = opt5.isPresent();
        System.out.println(present);

        //6.ifPresent():如果不为空执行的操作,传入消费型函数
        Optional<String> opt6 = Optional.ofNullable("11");
        opt6.ifPresent(System.out::println);

        //7. map():映射
        Optional<Person> opt7 = Optional.of(new Person("hello",18));
        if(opt7.isPresent()){
            Integer integer = opt7.map(Person::getName)
                    .map(String::length)
                    .orElse(0);
            System.out.println(integer);
        }
    }

    public static String t(){
        return "hello word";
    }
}
相关推荐
逊嘘4 分钟前
【Java语言】抽象类与接口
java·开发语言·jvm
morris13111 分钟前
【SpringBoot】Xss的常见攻击方式与防御手段
java·spring boot·xss·csp
七星静香36 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
Jacob程序员37 分钟前
java导出word文件(手绘)
java·开发语言·word
ZHOUPUYU37 分钟前
IntelliJ IDEA超详细下载安装教程(附安装包)
java·ide·intellij-idea
stewie641 分钟前
在IDEA中使用Git
java·git
Elaine2023911 小时前
06 网络编程基础
java·网络
G丶AEOM1 小时前
分布式——BASE理论
java·分布式·八股
落落鱼20131 小时前
tp接口 入口文件 500 错误原因
java·开发语言
想要打 Acm 的小周同学呀1 小时前
LRU缓存算法
java·算法·缓存