Java——JDK1.8新特性

目录

一、Lambda 表达式

(一)Lambda 表达式语法

(二)类型推断

二、函数式接口

(一)自定义函数式接口

(二)作为参数传递Lambda 表达式

(三)Java 内置四大核心函数式接口

三、方法引用

四、Stream API

(一)什么是Stream?

(二)Stream 的操作三个步骤

(三)创建流的四种方式

(四)Stream的中间操作

(五)Stream的终止操作

五、综合案例

六、新时间日期API

(一)使用LocalDate、LocalTime、LocalDateTime

(二)使用Instant时间戳

(三)Duration 和 Period

(四)日期的操纵

(五)解析与格式化

(六)时区的处理

(七)与传统日期处理的转换

七、接口中的默认方法与静态方法

八、其他新特性

(一)Optional 类

(二)重复注解与类型注解


一、Lambda 表达式

Lambda 是一个 匿名函数 ,我们可以把 Lambda表达式理解为是 一段可以传递的代码 (将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
案例:从匿名内部类---》Lambda表达式
    // 原来的匿名内部类
    @Test
    public void test1() {
        Comparator<Integer> com = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };

        TreeSet<Integer> ts = new TreeSet<>(com);
    }

    // Lambda表达式
    @Test
    public void test2() {
        Comparator<Integer> com = (o1, o2) -> Integer.compare(o1, o2);
        TreeSet<Integer> ts = new TreeSet<>(com);
    }

(一)Lambda 表达式语法

Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “ -> ” , 该操作符被称为 Lambda 操作符或剪头操作符。它将 Lambda 分为两个部分:
  • 左侧:指定了 Lambda 表达式需要的所有参数
  • 右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。

语法格式一:无参,无返回值,Lambda体只需一条语句

 语法格式二:Lambda需要一个参数

语法格式三:Lambda只需要一个参数时,参数的小括号可以省略 

语法格式四:Lambda需要两个参数,并且有返回值

语法格式五:当 Lambda 体只有一条语句时,return 与大括号可以省略

 (二)类型推断

上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的 “类型推断“

二、函数式接口

只包含一个抽象方法的接口,称为 函数式接口
  • 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
  • 我们可以在任意函数式接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。

(一)自定义函数式接口

(二)作为参数传递Lambda 表达式

作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接 收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口 的类型。

(三)Java 内置四大核心函数式接口

 

其他子接口(参数个数) 

/*
 * @Description Java8 内置四大核心函数式接口
 * Consumer<T> : 消费型接口
 *      void accept(T t)
 *
 * Supplier<T> : 供给型接口
 *      T get();
 *
 * Function<T, R> : 函数型接口
 *      R apply(T t)
 *
 * Predicate<T> : 断言型接口
 *      boolean test(T t)
 **/
public class TestLambda02 {

    /*
     * Predicate<T> : 断言型接口
     * 将长度大于等于3的字符串输出
     */

    @Test
    public void test04() {
        List<String> stringList = Arrays.asList("hello", "world","you");
        List<String> list = getStringList(stringList, (s) -> s.length() > 3);
        list.forEach(System.out::println);
    }
    public List<String> getStringList(List<String> stringList, Predicate<String> pre) {
        List<String> strings = new ArrayList<>();
        for (String s : stringList) {
            if(pre.test(s)) {
                strings.add(s);
            }
        }
        return strings;
    }
    /*
     * Function<T, R> : 函数型接口
     *
     */
    @Test
    public void test03() {
        String string = getString("\t\t\t 帅哥好帅", (str) -> str.trim());
        System.out.println(string);
    }
    public String getString(String str, Function<String ,String> func) {
        return func.apply(str);
    }

    /*
     * Supplier<T> : 供给型接口
     * 随机产生10个数字
     */
    @Test
    public void test02() {
        int num = 10;
        generator(num, () -> (int)(Math.random() * 100) + 1);
    }
    public void generator(int x, Supplier<Integer> sup) {
        List<Integer> integerList = new ArrayList<>();
        for(int i = 0; i < x; i ++) {
            Integer integer = sup.get();
            integerList.add(integer);
        }
        integerList.forEach(System.out::println);
    }

    /*
     * Consumer<T> : 消费型接口
     */
    @Test
    public void test01() {
        int num = 100;
        consumer(num, x -> System.out.println("消费了" + num + "元"));
    }
    public void consumer(int num, Consumer<Integer> com) {
        com.accept(num);
    }
}

三、方法引用

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!
方法引用:使用操作符 “ :: ” 将方法名和对象或类的名字分隔开来。
如下三种主要使用情况
  • 对象::实例方法
  • 类::静态方法
  • 类::实例方法
/*
 * 一、方法引用: 若Lambda体中的内容有方法已经实现了,我们可以通过方法引用
 *                  (即方法引用是Lambda表达式的另外一种表现形式)
 * 主要有三种语法格式:
 *
 * 对象::实例方法名
 * 类::静态方法名
 * 类::实例方法名
 *
 * 注意:Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的参数列表和返回值类型一致
 *
 * 二、构造器引用
 * 格式: ClassName::New
 *
 * 注意:需要调用的构造器的参数列表要与函数式接口中的抽象方法的参数列表一致!
 *
 * 三、数组引用
 * 格式:Type[]::new
 */
public class TestMethodRef {
    // 数组引用
    @Test
    public void test05() {
        Function<Integer, String[]> func = (x) -> new String[x];
        String[] strings = func.apply(10);
        System.out.println(strings.length);

        Function<Integer, String[]> func2 = String[]::new;
        String[] strs = func2.apply(20);
        System.out.println(strs.length);

    }

    // 构造器引用
    @Test
    public void test04() {
        Supplier<Employee> sup = () -> new Employee();
        // 构造器引用的方式
        Supplier<Employee> sup2 = Employee::new;
        Employee employee = sup2.get();
        System.out.println(employee);
        /*
         * 如何知道调用的是哪一个构造方法?
         * 根据函数式接口传入的参数的个数决定调用的构造器方法
         */
        Function<Integer, Employee> func = Employee::new;
        Employee employee1 = func.apply(20);
        System.out.println(employee1);

    }

    // 类::实例方法名
    @Test
    public void tes03() {
        // 规则:若Lambda参数列表中的第一参数是实例方法的调用者,第二参数是实例方法的参数时,此时
        //      可以通过 class::method
        BiPredicate<String ,String> bp = (x, y) -> x.equals(y);

        BiPredicate<String ,String> bp1 = String::equals;
    }

    // 对象::静态方法
    @Test
    public void  test02() {
        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
        Comparator<Integer> com1 = Integer::compare;

    }

    // 对象::实例方法
    @Test
    public void test01() {
        PrintStream ps = System.out;
        Consumer<String> con = (x) -> System.out.println(x);
        Consumer<String> con2 = ps::println;
    }
}

 四、Stream API

Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API(java.util.stream.*) 。 Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

(一)什么是Stream?

流(Stream) 到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算!”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

 (二)Stream 的操作三个步骤

  • 创建 Stream
        一个数据源(如:集合、数组),获取一个流
  • 中间操作
        一个中间操作链,对数据源的数据进行处理
  • 终止操作(终端操作)
        一个终止操作,执行中间操作链,并产生结果

(三)创建流的四种方式

方式一:Collection接口

Java8 中的 Collection 接口被扩展,提供了
两个获取流的方法
default Stream<E> stream() : 返回一个顺序流
default Stream<E> parallelStream() : 返回一个并行流

方式二:数组创建流

Java8 中的 Arrays 的静态方法 stream() 可
以获取数组流:
static <T> Stream<T> stream(T[] array): 返回一个流
重载形式,能够处理对应基本类型的数组:
public static IntStream stream(int[] array)
public static LongStream stream(long[] array)
public static DoubleStream stream(double[] array)
方式三:由值创建流
可以使用静态方法 Stream.of(), 通过显示值
创建一个流。它可以接收任意数量的参数。
public static<T> Stream<T> of(T... values) : 返回一个流
方式四:由函数创建流
可以使用静态方法 Stream.iterate() 和
Stream.generate(), 创建无限流。
迭代
public static<T> Stream<T> iterate(final T seed, final
UnaryOperator<T> f)
生成
public static<T> Stream<T> generate(Supplier<T> s) :
/*
 * 一、Stream的三个操作
 * ① 创建Stream流对象
 * ② 中间操作
 * ③ 终止操作
 */
public class testStream {
    // 创建stream
    @Test
    public void test1() {
        //1.可以通过Collection 系列集合提供的stream() or parallelStream()
        List<Integer> list = new ArrayList<>();
        Stream<Integer> stream1 = list.stream();
        //2. 通过Arrays的静态方法stream()获取数组流
        Employee[] emps = new Employee[2];
        Stream<Employee> stream2 = Arrays.stream(emps);
        //3. 通过stream的静态方法of()
        Stream<Integer> stream3 = Stream.of(1, 2, 3);
        //4. 创建无限流
        // 迭代
        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
        stream4.limit(10).forEach(System.out::println);

        // 生成
        Stream<Double> stream5 = Stream.generate(Math::random);
        stream5.limit(10).forEach(System.out::println);
    }

}

 (四)Stream的中间操作

多个 中间操作 可以连接起来形成一个 流水线 ,除非流水线上触发终止操作,否则 中间操作不会执行任何的处理 !而在 终止操作时一次性全部处理,称为“惰性求值”
筛选与切片

 映射

排序

/*
 * 一、Stream的三个操作
 * ① 创建Stream流对象
 * ② 中间操作
 * ③ 终止操作
 */
public class testStreamApi {

    List<Employee> empList = Arrays.asList(
            new Employee("张三", 50, 7777.77),
            new Employee("李四", 35, 8888.6),
            new Employee("王五", 20, 999.55),
            new Employee("赵六", 40, 1000.5),
            new Employee("赵六", 40, 1000.5),
            new Employee("赵六", 40, 1000.5));
    // 中间操作
    /*
     排序
    sorted()-自然排序(Comparable)
    sorted(Comparator com)---定制排序(Comparator)
     */
    @Test
    public void test06() {
        List<String> strs = Arrays.asList("aaa", "bbb", "ccc");
        strs.stream()
                .sorted()  // 按已经实现的comparator接口的排序规则进行排序称为自然排序
                .forEach(System.out::println);

        // 先按年龄排序,然后按姓名排序
        empList.stream()
                .sorted((x, y) -> {
                    if(x.getAge().equals(y.getAge())) {
                        return x.getName().compareTo(y.getName());
                    }
                    return x.getAge().compareTo(y.getAge());
                })
                .forEach(System.out::println);
    }
    /*
        映射
        map一接收 Lambda ,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被用到每个元素上,并将其映射成一个新的元素
        flatMap一接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
     */
    @Test
    public void test05() {
        List<String> stringList = Arrays.asList("aaa", "bbb", "ccc");
        stringList.stream()
                .map((str) -> str.toUpperCase())
                .forEach(System.out::println);
        System.out.println("---------------------------------");
        // 获取员工的名字
        empList.stream()
                .distinct()
                .map(Employee::getName)
                .forEach(System.out::println);
        System.out.println("---------------------------------");
        // map实现将每个字符串转化为字符  复杂
        Stream<Stream<Character>> streamStream = stringList.stream()
                .map(testStreamApi::filterCharacter);
        streamStream.forEach((sm) -> {
            sm.forEach(System.out::println);
        });
        System.out.println("---------------------------------");
        // flatMap实现将每个字符串转化为字符 简单
        Stream<Character> stream = stringList.stream()
                .flatMap(testStreamApi::filterCharacter);
        stream.forEach(System.out::println);
    }
    public static Stream<Character> filterCharacter(String str) {
        List<Character> list = new ArrayList<>();
        for(Character ch : str.toCharArray()) {
            list.add(ch);
        }
        return list.stream();
    }

    /*
    筛选与切片
    filter-接收 Lambda ,从流中排除某些元素。
    limit-截断流,使其元素不超过给定数量。
    skip(n) - 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
    distinct-筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
     *
     */
    @Test
    public void test04() {
        empList.stream()
                .filter((e) -> e.getAge() > 30)
                .skip(2)
                .distinct() // 这里是根据hashCode和equals,所以employee需要重写hashCode和equals
                .forEach(System.out::println);
    }
    @Test
    public void test03() {
        empList.stream()
                .filter((e) -> e.getAge() > 30)
                .limit(2)
                .forEach(System.out::println);
    }
    // 内部迭代:即stream内部帮我们迭代
    @Test
    public void test01() {
        // 中间操作
        Stream<Employee> stream = empList.stream()
                .filter((e) -> e.getAge() > 30);
        // 终止操作:一次性执行全部内容,即“惰性求值”
        stream.forEach(System.out::println);

    }
    // 外部迭代
    @Test
    public void test02() {
        Iterator<Employee> iterator = empList.iterator();
        while(iterator.hasNext()) {
            Employee employee = iterator.next();
            if(employee.getAge() > 30) {
                System.out.println(employee);
            }

        }
    }
}

 (五)Stream的终止操作

Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

public class testStreamApi2 {
    List<Employee> empList = Arrays.asList(
            new Employee("张三", 50, 7777.77, Employee.Status.FREE),
            new Employee("李四", 35, 8888.6, Employee.Status.BUSY),
            new Employee("王五", 20, 999.55, Employee.Status.VOCATION),
            new Employee("赵六", 40, 1000.5, Employee.Status.BUSY),
            new Employee("赵六", 40, 1000.5, Employee.Status.FREE),
            new Employee("赵六", 40, 1000.5, Employee.Status.VOCATION));

    /*
     收集
    collect一将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
     */

    // 其他
    @Test
    public void test09() {
        DoubleSummaryStatistics ssd = empList.stream()
                .collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(ssd.getAverage());
        System.out.println(ssd.getMax());
        System.out.println(ssd.getMin());
        String str = empList.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(",", "===", "==="));
        System.out.println(str);
    }
    // 分区
    @Test
    public void test08() {
        // 分成true和false两个区
        Map<Boolean, List<Employee>> map = empList.stream()
                .collect(Collectors.partitioningBy((e) -> e.getSalary() > 5000));
        System.out.println(map);
    }
    // 多级分组
    @Test
    public void test07() {
        Map<Employee.Status, Map<String, List<Employee>>> map = empList.stream()
                .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
                    if (e.getAge() < 30) {
                        return "青年";
                    } else if (e.getAge() < 50) {
                        return "中年";
                    } else {
                        return "老年";
                    }
                })));
        System.out.println(map);
    }

    // 分组
    @Test
    public void test06() {
        Map<Employee.Status, List<Employee>> map = empList.stream()
                .collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(map);
    }
    @Test
    public void test05() {
        Long size = empList.stream()
                .collect(Collectors.counting());
        System.out.println(size);

        Double avg = empList.stream()
                .collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avg);

        Double sum = empList.stream().collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sum);

        Optional<Double> max = empList.stream()
                .map(Employee::getSalary)
                .collect(Collectors.maxBy(Double::compareTo));
        System.out.println(max.get());

        Optional<Employee> min = empList.stream()
                .collect(Collectors.minBy((x, y) -> Double.compare(x.getSalary(), y.getSalary())));
        System.out.println(min.get());
    }
    @Test
    public void test04() {
        List<String> list1 = empList.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());
        list1.forEach(System.out::println);
        System.out.println("------------------------");
        Set<String> set = empList.stream()
                .map(Employee::getName)
                .collect(Collectors.toSet());
        set.forEach(System.out::println);
        System.out.println("------------------------");
        LinkedHashSet<String> hashSet = empList.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(LinkedHashSet::new));
        hashSet.forEach(System.out::println);
    }
    /*
     规约
    reduce(T identity,BinaryOperator) / reduce(BinaryOperator)
                                ——可以将流中元素反复结合起来,得到一个值。
    */
    @Test
    public void test03() {
        List<Integer> list = Arrays.asList(1, 2, 3);
        Integer sum1 = list.stream()
                .reduce(1, (x, y) -> x + y);

        System.out.println(sum1);

        Optional<Double> sum2 = empList.stream()
                .map(Employee::getSalary)
                .reduce(Double::sum);
        System.out.println(sum2.get());
    }
    /*
     查找与匹配
     allMatch-检查是否匹配所有元素
     anyMatch-检查是否至少匹配一个元素
     noneMatch-检查是否没有匹配所有元素
     findFirst-返回第一个元素
     findAny-返回当前流中的任意元素
     count-返回流中元素的总个数
     max一返回流中最大值
     min-返回流中最小值
     */
    @Test
    public void test02() {
        // 数量
        long count = empList.stream()
                .count();
        System.out.println(count);
        // 最大值
        Optional<Employee> max = empList.stream()
                .max((x, y) -> Double.compare(x.getSalary(), y.getSalary()));
        System.out.println(max.get().getSalary());
        // 最小值
        Optional<Double> min = empList.stream()
                .map(Employee::getSalary)
                .min(Double::compareTo);
        System.out.println(min.get());

    }
    @Test
    public void test01() {
        boolean b1 = empList.stream()
                .allMatch((e) -> e.getStatus().equals(Employee.Status.FREE));
        System.out.println(b1);

        boolean b2 = empList.stream()
                .anyMatch((e) -> e.getStatus().equals(Employee.Status.FREE));
        System.out.println(b2);

        boolean b3 = empList.stream()
                .noneMatch((e) -> e.getStatus().equals(Employee.Status.FREE));
        System.out.println(b3);

        // 返回的值有可能为空所以封装到了Optional
        Optional<Employee> op1 = empList.stream()
                .sorted((x, y) -> Double.compare(x.getSalary(), y.getSalary()))
                .findFirst();
        System.out.println(op1.get());

        Optional<Employee> op2 = empList.stream()
                .sorted((x, y) -> Double.compare(x.getSalary(), y.getSalary()))
                .findAny();
        System.out.println(op2.get());


    }
}

 案例:

public class testStreamApi3 {
    /*
     * 1。给定一个数字列表,如何返回一个由每个数的平方构成的列表呢?
        给定[1,2,3,4,5],应该返回[1,4,9,16,25]。
     */
    @Test
    public void test() {
        Integer[] num = new Integer[]{1, 2, 3, 4, 5};
        Stream<Integer> stream = Arrays.stream(num);
        stream.map(x -> x*x).forEach(System.out::println);

    }
    List<Employee> empList = Arrays.asList(
            new Employee("张三", 50, 7777.77),
            new Employee("李四", 35, 8888.6),
            new Employee("王五", 20, 999.55),
            new Employee("赵六", 40, 1000.5),
            new Employee("赵六", 40, 1000.5),
            new Employee("赵六", 40, 1000.5));
    /*
     * 如何看流中有多少个employee
     * 用map和reduce实现
     */
    @Test
    public void test2() {
        Optional<Integer> sum = empList.stream()
                .map(e -> 1)
                .reduce(Integer::sum);
        System.out.println(sum.get());

    }
}

 五、综合案例

public interface MyPredicate<T> {
    public boolean test(Employee employee);
}
====================================================================


    List<Employee> empList = Arrays.asList(
        new Employee("张三", 50, 7777.77),
        new Employee("李四", 35, 8888.6),
        new Employee("王五", 20, 999.55),
        new Employee("赵六", 40, 1000.5)
    );
    // 需求:获取当前公司中员工年龄大于35的员工
    @Test
    public void test3() {
        List<Employee> employees = filterList(empList);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
        System.out.println("---------------------------------");
        employees = filterListBySalary(empList);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }
    public List<Employee> filterList(List<Employee> empList) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : empList) {
            if(emp.getAge() > 35) {
                emps.add(emp);
            }
        }
        return emps;
    }
    // 需求:获取当前公司中员工工资大于4000的员工
    public List<Employee> filterListBySalary(List<Employee> empList) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : empList) {
            if(emp.getSalary() > 4000) {
                emps.add(emp);
            }
        }
        return emps;
    }

    // 优化方式一:策略设计模式
    @Test
    public void test4() {
        List<Employee> employees
                = filterList(empList, new filterEmployeeByAge());
        for (Employee employee : employees) {
            System.out.println(employee);
        }

        System.out.println("--------------------------------");
        employees
                = filterList(empList, new filterEmployeeBySalary());
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }
    public List<Employee> filterList(List<Employee> empList, MyPredicate<Employee> pre) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : empList) {
            if(pre.test(emp)) {
                emps.add(emp);
            }
        }
        return emps;
    }
    // 优化方式二:匿名内部类
    @Test
    public void test5() {
        List<Employee> emps = filterList(empList, new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee employee) {
                return employee.getSalary() > 4000;
            }
        });
        emps.forEach(System.out::println);
    }
    // 优化方式三:Lambda表达式
    @Test
    public void test6() {
        List<Employee> employees = filterList(empList, e -> e.getSalary() > 4000);
        employees.forEach(System.out::println);
    }

    // 优化方式四:streamApi
    @Test
    public void test7() {
        empList.stream()
                .filter(e -> e.getSalary() > 4000)
                .limit(1)
                .forEach(System.out::println);
        System.out.println("-----------------------");
        empList.stream()
                .map(Employee::getName)
                .forEach(System.out::println);
    }

六、新时间日期API

原本的时间相关的类存在线程安全问题

public class testSimpleDateFormat {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");

        Callable<Date> task = new Callable<Date>() {
            @Override
            public Date call() throws Exception {
                return sdf.parse("20230518");
            }
        };

        ExecutorService pool = Executors.newFixedThreadPool(10);
        List<Future<Date>> results = new ArrayList<>();
        for(int i = 0; i < 10; i ++) {
            results.add(pool.submit(task));
        }

        for(Future<Date> future:results) {
            System.out.println(future.get());
        }

        pool.shutdown();
    }
}

以前的方式,加锁,这里使用ThreadLocal

public class DateFormatThreadLocal {
    private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
            protected DateFormat initialValue() {
                return new SimpleDateFormat("yyyyMMdd");
            }
    };
    public static Date convert(String source) throws ParseException {
        return df.get().parse(source);
    }
}

=========================================================================

public class testSimpleDateFormat {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");

        Callable<Date> task = new Callable<Date>() {
            @Override
            public Date call() throws Exception {
//                return sdf.parse("20230518");
                return DateFormatThreadLocal.convert("20230518");
            }
        };

        ExecutorService pool = Executors.newFixedThreadPool(10);
        List<Future<Date>> results = new ArrayList<>();
        for(int i = 0; i < 10; i ++) {
            results.add(pool.submit(task));
        }

        for(Future<Date> future:results) {
            System.out.println(future.get());
        }
        pool.shutdown();
    }
}

 jdk1.8的时间类

public class testSimpleDateFormat {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");

        Callable<LocalDate> task = new Callable<LocalDate>() {
            @Override
            public LocalDate call() throws Exception {
                return LocalDate.parse("20230518", dtf);
            }
        };

        ExecutorService pool = Executors.newFixedThreadPool(10);
        List<Future<LocalDate>> results = new ArrayList<>();
        for(int i = 0; i < 10; i ++) {
            results.add(pool.submit(task));
        }

        for(Future<LocalDate> future:results) {
            System.out.println(future.get());
        }
        pool.shutdown();
    }
}

(一)使用LocalDateLocalTimeLocalDateTime

LocalDate LocalTime LocalDateTime 类的实例是 不可变的对象 ,分别表示使用 ISO-8601
历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。

 

(二)使用Instant时间戳

用于“时间戳”的运算。它是以 Unix 元年 ( 传统的设定为UTC 时区 1970 1 1 日午夜时分 ) 开始所经历的描述进行运算

(三)Duration Period

Duration: 用于计算两个“时间”间隔
Period: 用于计算两个“日期”间隔

(四)日期的操纵

TemporalAdjuster : 时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
TemporalAdjuster s : 该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现。

(五)解析与格式化

java.time.format.DateTimeFormatter 类:该类提供了三种
格式化方法:
预定义的标准格式
语言环境相关的格式
自定义的格式

(六)时区的处理

Java8 中加入了对时区的支持,带时区的时间为分别为:
ZonedDate ZonedTime ZonedDateTime
其中每个时区都对应着 ID ,地区 ID 都为 “{区域 }/{ 城市 } ”的格式
例如 : Asia/Shanghai
ZoneId :该类中包含了所有的时区信息
        getAvailableZoneIds() : 可以获取所有时区时区信息
        of(id) : 用指定的时区信息获取 ZoneId 对象

(七)与传统日期处理的转换

public class testLocalDateTime {
    // ZonedDate/ZonedTime/ZonedDateTime 时区相关的
    @Test
    public void test8() {
        // 获得的是指定时区的当前时间
        LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        System.out.println(ldt);

        LocalDateTime ldt2 = LocalDateTime.now();
        ZonedDateTime zdt = ldt2.atZone(ZoneId.of("Asia/Shanghai"));
        // 输出2023-05-18T22:01:45.684+08:00[Asia/Shanghai]
        // +08:00 是距离UTC的时间差
        System.out.println(zdt);

    }
    @Test
    public void test7() {
        // 获取支持的时区
        Set<String> set = ZoneId.getAvailableZoneIds();
        set.forEach(System.out::println);
    }
    // DateTimeFormatter : 格式化时间/日期
    @Test
    public void test6() {
        DateTimeFormatter dtf1 = DateTimeFormatter.ISO_DATE;
        LocalDateTime ldt1 = LocalDateTime.now();
        System.out.println(ldt1.format(dtf1));

        System.out.println("-------------------------------");
        // 格式化
        DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
        String time = ldt1.format(dtf2);
        System.out.println(time);
        // 再格式化回去
        LocalDateTime ldt2 = ldt1.parse(time, dtf2);
        System.out.println(ldt2);
    }
    // TemporalAdjuster: 时间矫正器
    @Test
    public void test5() {
        LocalDateTime ldt1 = LocalDateTime.now();
        System.out.println(ldt1);

        // 直接设置日期为哪一天,不太方便求下一个周几等的操作
        LocalDateTime ldt2 = ldt1.withDayOfMonth(10);
        System.out.println(ldt2);

        // 求下一个星期日
        LocalDateTime ldt3 = ldt1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
        System.out.println(ldt3);

        //自定义:下一个工作日
        LocalDateTime ldt5 = ldt1.with((l) -> {
            LocalDateTime ldt4 = (LocalDateTime)l;
            DayOfWeek dayOfWeek = ldt4.getDayOfWeek();
            if(dayOfWeek.equals(DayOfWeek.FRIDAY)) {
                return ldt4.plusDays(3);
            } else if(dayOfWeek.equals(DayOfWeek.SATURDAY)) {
                return ldt4.plusDays(2);
            } else {
                return ldt4.plusDays(1);
            }
        });
        System.out.println(ldt5);
    }

    // 3、 Duration: 计算两个“时间”之间的间隔
    //     Period: 计算两个“日期”之间的间隔
    @Test
    public void test4() {
        LocalDate l1 = LocalDate.of(2022, 5, 17);
        LocalDate l2 = LocalDate.now();
        Period period = Period.between(l1, l2);
        System.out.println(period);

        System.out.println(period.getYears());
        System.out.println(period.getMonths());
        System.out.println(period.getDays());
    }
    @Test
    public void test3() throws InterruptedException {
        Instant i1 = Instant.now();
        Thread.sleep(1000);
        Instant i2 = Instant.now();
        System.out.println(Duration.between(i1, i2));
        System.out.println("----------------------------");
        LocalTime l1 = LocalTime.now();
        Thread.sleep(1000);
        LocalTime l2 = LocalTime.now();
        System.out.println(Duration.between(l1, l2));
    }

    // 2、Instant :时间戳(以Unix元年:1970年1月1日00:00:00到某个时间之间的毫秒数)
    @Test
    public void test2() {
        Instant i1 = Instant.now(); // 默认获取UTC时区
        System.out.println(i1);

        // 与中国相差八个时区,可设置偏移
        OffsetDateTime time = i1.atOffset(ZoneOffset.ofHours(8));
        System.out.println(time);

        // 从 1970-现在 的秒数
        long second = i1.getEpochSecond();
        System.out.println(second);

        // 从元年开始计算,这里是+60s
        Instant i2 = Instant.ofEpochSecond(60);
        System.out.println(i2);
    }


    // 1、LocalDate LocalTime LocalDateTime(前两个的结合体)
    // 一个会用另外两个也差不多了
    @Test
    public void test1() {
        // 返回当前年月日时分秒毫秒
        LocalDateTime ldt1 = LocalDateTime.now();
        System.out.println(ldt1);

        // 构造日期
        LocalDateTime ldt2 = LocalDateTime.of(2023, 5, 18, 20, 2, 20);
        System.out.println(ldt2);

        // 加年数
        LocalDateTime ldt3 = ldt1.plusYears(2);
        System.out.println(ldt3);

        // 减年数
        LocalDateTime ldt4 = ldt1.minusYears(2);
        System.out.println(ldt4);
        // 取详细信息
        System.out.println(ldt1.getYear());
        System.out.println(ldt1.getMonthValue());
        System.out.println(ldt1.getDayOfMonth());
        System.out.println(ldt1.getHour());
        System.out.println(ldt1.getMinute());
        System.out.println(ldt1.getNano());
    }

}

 七、接口中的默认方法与静态方法

Java 8中允许接口中包含具有具体实现的方法,该方法称为“默认方法” ,默认方法使用 default 关键字修饰。
接口默认方法的”类优先”原则
若一个接口中定义了一个默认方法,而另外一个父类或接口中
又定义了一个同名的方法时
选择父类中的方法。如果一个父类提供了具体的实现,那么
接口中具有相同名称和参数的默认方法会被忽略。
接口冲突。如果一个父接口提供一个默认方法,而另一个接
口也提供了一个具有相同名称和参数列表的方法(不管方法
是否是默认方法),那么必须覆盖该方法来解决冲突

Java8 中,接口中允许添加静态方法。

public class MyClass {
    public String getName() {
        return "father";
    }
}
===========================================================================
public interface MyFun {
    default String getName() {
        return "hahaha";
    }
}
===========================================================================
public interface MyInterface {
    default String getName() {
        return "heiheihei";
    }
}
===========================================================================
public class SubClass extends MyClass implements MyFun, MyInterface {
    // 两个接口中都有同名函数时,需要实现方法指定执行哪一个接口中的函数
// 当父类中也有同名函数而子类中没有时,默认调用父类函数,忽略接口中的默认函数
}

===========================================================================
public class testDefault {
    public static void main(String[] args) {

        SubClass subClass = new SubClass();
        System.out.println(subClass.getName());
    }
}

public class SubClass extends MyClass implements MyFun, MyInterface {
    // 两个接口中都有同名函数时,需要实现方法指定执行哪一个接口中的函数
// 当父类中也有同名函数而子类中没有时,默认调用父类函数,忽略接口中的默认函数
    @Override
    public String getName() {
        return MyInterface.super.getName();
    }
}

public interface MyInterface {
    default String getName() {
        return "heiheihei";
    }

    public static void show() {
        System.out.println("展示");
    }
}
==================================================
public class testDefault {
    public static void main(String[] args) {

        SubClass subClass = new SubClass();
        System.out.println(subClass.getName());
        // 调用接口中的静态方法
        MyInterface.show();
    }
}

八、其他新特性

(一)Optional

Optional<T> 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
常用方法:
  • Optional.of(T t) : 创建一个 Optional 实例
  • Optional.empty() : 创建一个空的 Optional 实例
  • Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
  • isPresent() : 判断是否包含值
  • orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
  • orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
  • map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
  • flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
public class testOptional {

    /*
     Optional 容器类的常用方法:
    Optional.of(T t) : 创建一个 Optional 实例
    Optional.empty() : 创建一个空的 Optional 实例
    Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
    isPresent() : 判断是否包含值
    orElse(T t) :  如果调用对象包含值,返回该值,否则返回t
    orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
    map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional,empty()
    flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
     */
    // 为什么说它方便了空指针异常的调试,因为这里出现空值会报错NoSuchElementException

    @Test
    public void test6() {
        Optional<Employee> optional = Optional.ofNullable(new Employee("张三", 18, 50000.0, Employee.Status.BUSY));
//        Optional<String> name = optional.map((e) -> optional.get().getName());
//        System.out.println(name.get());
        Optional<String> name = optional.flatMap(e -> Optional.of(e.getName()));
        System.out.println(name.get());

    }
    @Test
    public void test5() {
        Optional<Employee> optional = Optional.ofNullable(null);

        // 如果optional为空,那么返回传进去的默认参数,否则返回optional的参数
        Employee employee1 = optional.orElse(new Employee());
        System.out.println(employee1);
        // 使用供给型函数式接口,如果optional为空,那么返回传进去的默认参数,否则返回optional的参数
        Employee employee2 = optional.orElseGet(Employee::new);
        System.out.println(employee2);
    }
    @Test
    public void test4() {
        Optional<Employee> optional = Optional.ofNullable(null);
//        System.out.println(optional.get()); // 报错NoSuchElementException

        // 安全的做法,判断是否包含值
        if(optional.isPresent()) {
            System.out.println(optional.get());
        }
    }
    @Test
    public void test3() {
        Optional<Employee> optional = Optional.empty();
        System.out.println(optional.get()); // 报错NoSuchElementException
    }

    @Test
    public void test2() {
        Optional<Employee> optional = Optional.of(null); // 报错NullPointerException
    }
    @Test
    public void test1() {
        Optional<Employee> optional = Optional.of(new Employee());
        System.out.println(optional.get());

    }
}

(二)重复注解与类型注解

Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。
@Repeatable(MyAnnotaions.class) // 指明容器类
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotaion {
    String value() default "atguigu";
}
===============================================================================
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotaions {
    MyAnnotaion[] value();
}
===============================================================================


/*
 * 重复注解与类型注解
 */
public class TestAnnotaion {

    @Test
    public void test1() throws Exception {
        Class<TestAnnotaion> aClass = TestAnnotaion.class;
        Method method =
                aClass.getMethod("show");

        MyAnnotaion[] annotations = method.getAnnotationsByType(MyAnnotaion.class);
        for(MyAnnotaion mya: annotations) {
            System.out.println(mya);
        }


    }

    @MyAnnotaion("world")
    @MyAnnotaion("hello")
    public void show(@MyAnnotaion("abc") String abs) {

    }
}

猜你喜欢

转载自blog.csdn.net/m0_62946761/article/details/130753016