• JAVA8 新特性简介


    特点:

    速度更快(HashMap加哈希表,ConcurrentHashMap使用CAS,内存结构无永久区、新增元数据区使用物理内存)
    代码更少(增加了新的语法 Lambda 表达式)
    强大的 Stream API
    便于并行(优化ForkJoin)
    //JDK8之前 需要自己实现计算过程,下面的省略部分代码
    public class ForkJoinCalculate extends RecursiveTask<Long> {
    private long start;
    private long end;
    @Override
    protected Long compute() {}
    }
    ForkJoinTask<Long> task = new ForkJoinCalculate(0L, 10000000000L);
    long sum = new ForkJoinPool().invoke(task);

    //JDK8 之后
    Long sum = LongStream.rangeClosed(0L, 10000000000L).parallel().sum();
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    最大化减少空指针异常 Optional
    1. Lambda 表达式
    1.1 为什么使用 Lambda 表达式
    Lambda 是一个 匿名函数,我们可以把 Lambda表达式理解为是 一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
    示例1:

    //匿名类实现
    Runnable r = new Runnable() {
    @Override
    public void run() {
    System.out.println("Hello World!" );
    }
    };
    //Lambda 表达式实现
    Runnable r1 = () -> System.out.println("Hello Lambda!");
    1
    2
    3
    4
    5
    6
    7
    8
    9
    示例2:

    Comparator<String> com = new Comparator<String>(){
    @Override
    public int compare(String o1, String o2) {
    return Integer.compare(o1.length(), o2.length());
    }
    };

    //Lambda 表达式实现
    Comparator<String> com = (x, y) -> Integer.compare(x.length(), y.length());
    1
    2
    3
    4
    5
    6
    7
    8
    9
    1.2 Lambda 表达式语法
    Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “- -> >” , 该操作符被称为 Lambda 操作符或剪头操作符。它将 Lambda 分为两个部分:

    左侧:指定了 Lambda 表达式需要的所有参数
    右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。
    常用的几种语法格式:

    无参数,无返回值
    () -> System.out.println("Hello Lambda!");
    1
    有一个参数, 无返回值
    (x) -> System.out.println("Hello " + x);
    1
    只有一个参数,小括号可以省略不写
    x -> System.out.println("Hello " + x);
    1
    两个以上的参数,有返回值,并且 Lambda 体中有多条语句
    Comparator<Integer> com = (x, y) -> {
    System.out.println("函数式接口");
    return Integer.compare(x, y);
    };
    1
    2
    3
    4
    Lambda 体中只有一条语句, return 和 大括号都可以省略不写
    Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
    1
    1.3 类型推断
    上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。

    (x, y) -> Integer.compare(x, y);
    //等价于
    (Integer x, Integer y) -> Integer.compare(x, y);
    1
    2
    3
    2. 函数式接口
    Lambda 表达式需要“函数式接口”的支持。

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

    2.2 自定义函数式接口
    1)明确入参和出参的函数式接口

    @FunctionalInterface
    public interface MyFun {
    public Integer getValue(Integer num);
    }
    1
    2
    3
    4
    2) 带有泛型的函数式接口

    @FunctionalInterface
    public interface MyFunction<T, R> {
    public R getValue(T t);
    }
    1
    2
    3
    4
    3)使用自定义函数接口

    public void test(){
    Integer num = operation(100, (x) -> x * x);
    num = operation(200, (y) -> y + 200;
    }
    public Integer operation(Integer num, MyFun mf){
    return mf.getValue(num);
    }
    1
    2
    3
    4
    5
    6
    7
    为了将 Lambda 表达式作为参数传递,接收 Lambda 该 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。

    2.3 Java 内置的函数式接口
    4大核心接口:

    Consumer 消费型接口 对类型为T的对象应用操作
    方法:void accept(T t)
    @Test
    public void testConsumer() {
    consumeMoney(9999, (m) -> System.out.println("买花消费,每次消费:" + 9999));
    }

    public void consumeMoney(double money, Consumer<Double> con) {
    con.accept(money);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    Supplier 供给型接口 返回类型为T的对象
    方法:T get();
    @Test
    public void testSupplier() {
    List<Integer> numList = getNumList(6, () -> (int) (Math.random() * 100));
    numList.forEach(System.out::println);
    }

    // 产生指定个数的对象,并放入集合中
    public <T> List<T> getNumList(int num, Supplier<T> sup) {
    List<T> list = new ArrayList<>(num);
    for (int i = 0; i < num; i++) {
    list.add(sup.get());
    }
    return list;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Function<T, R> 函数型接口 对类型为T的对象应用操作,并返回R类型结果
    方法:R apply(T t);
    @Test
    public void testFunction() {
    String subStr = strHandler("Function<T, R> 函数型接口", (str) -> str.substring(15));
    System.out.println(subStr);
    }

    // 用于处理字符串
    public String strHandler(String str, Function<String, String> fun) {
    return fun.apply(str);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Predicate 断定型接口 确定类型为T的对象是否满足某约束
    方法: boolean test(T t);
    @Test
    public void testPredicate() {
    List<String> list = Arrays.asList("Hello", "Predicate", "Lambda");
    List<String> strList = filterStr(list, (s) -> s.length() > 5);
    strList.forEach(System.out::println);
    }

    // 将满足条件的字符串,放入集合中
    public List<String> filterStr(List<String> list, Predicate<String> pre) {
    List<String> strList = new ArrayList<>(list);
    strList.removeIf(str -> !pre.test(str));
    return strList;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    其他接口:

    接口 方法 描述
    BiFunction R apply(T t, U u); 对类型为 T, U 参数应用操作,返回 R 类型的结果。
    UnaryOperator T apply(T t); 对类型为T的对象进行一元运算,并返回T类型的结果(Function 子接口)。
    BinaryOperator T apply(T t1, T t2); 对类型为T的对象进行二元运算,并返回T类型的结果 (n BiFunction 子接口)。
    BiConsumer void accept(T t, U u) 对类型为T, U 参数应用操作。
    ToIntFunction int applyAsInt(T value); 对类型为T的参数计算返回int。
    ToLongFunction long applyAsInt(T value); 对类型为T的参数计算返回long。
    ToDoubleFunction double applyAsInt(T value); 对类型为T的参数计算返回double。
    IntFunction R apply(int value); 对类型为int参数操作。
    LongFunction R apply(long value); 对类型为long参数操作。
    DoubleFunction R apply(double value); 对类型为double参数操作。
    3. 方法、构造器和数组引用
    3.1 方法引用
    当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
    (实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)
    方法引用语法:使用操作符 “::” 将方法名和对象或类的名字分隔开来。
    三种常用语法 :

    对象 :: 实例方法
    @Test
    public void testObjectInstance() {
    PrintStream ps = System.out;
    Consumer<String> conL = (str) -> ps.println(str);
    conL.accept("Hello Lambda!");
    Consumer<String> conR = ps::println;
    conR.accept("Hello Method!");
    String str = "Hello Method";
    Supplier<String> supplier = str::toLowerCase;
    ps.println(supplier.get());
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    类 :: 静态方法
    @Test
    public void testClassStatic() {
    Comparator<Integer> comL = (x, y) -> Integer.compare(x, y);
    Comparator<Integer> comR = Integer::compare;
    }
    1
    2
    3
    4
    5
    类 :: 实例方法
    @Test
    public void testClassInstance() {
    BiPredicate<String, String> bpL = (x, y) -> x.equals(y);
    System.out.println(bpL.test("Lambda", "Lambda"));

    BiPredicate<String, String> bpR = String::equals;
    System.out.println(bpR.test("Reference", "Reference"));
    }
    1
    2
    3
    4
    5
    6
    7
    8
    方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!
    若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName。

    3.2 构造器引用
    格式: 类名 :: new
    与函数式接口相结合,自动与函数式接口中方法兼容。
    可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!

    @Test
    public void testConstructor() {
    // 调用无参构造器
    Supplier<String> supStr = String::new;
    System.out.println(supStr.get());
    // 调用有参构造器
    Function<String, String> funStr = String::new;
    System.out.println(funStr.apply("Constructor"));
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    3.3 数组引用
    格式: 类型[] :: new
    类型可以是基本数据类型和对象类型。

    @Test
    public void testArray() {
    // Lambda
    Function<Integer, String[]> funL = (args) -> new String[args];
    String[] strs = funL.apply(10);
    System.out.println(strs.length);
    // 数组引用
    Function<Integer, String[]> funR = String[]::new;
    String[] emps = funR.apply(20);
    System.out.println(emps.length);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    4. Stream API
    Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

    4.1 什么是 Stream
    流 (Stream) 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算! ”
    注意:

    Stream 自己不会存储元素。
    Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
    Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
    4.2 Stream的三个操作步骤
    创建Stream: 一个数据源(如:集合、数组),获取一个流
    中间操作: 一个中间操作链,对数据源的数据进行处理
    终止操作: 一个终止操作,执行中间操作链,并产生结果
    4.2.1 创建Stream的方式

    使用集合创建
    Java8 中的 Collection 接口被扩展,提供了两个获取流的方法 :
    1)default Stream stream() ;
    2) default Stream parallelStream() ; 返回一个并行流
    由数组创建流
    Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
    public static Stream 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 Stream of(T… values) ;
    由函数创建流:创建无限流
    可以使用静态方法 Stream.iterate() 和Stream.generate(), 创建无限流。
    1)迭代
    public static Stream iterate(final T seed, final UnaryOperator f);
    2) 生成
    public static Stream generate(Supplier s);
    4.2.2 Stream 的中间操作
    多个 中间操作可以连接起来形成一个 流水线,除非流水线上触发终止操作,否则 中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。常用中间操作类型:
    1)筛选与切片
    filter(Predicate p) 接收 Lambda , 从流中排除某些元素。
    distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
    limit(long maxSize) 截断流,使其元素不超过给定数量,获取到需要的数量之后就不会继续中间操作了。
    skip(long n)跳过元素,返回一个跳过了前 n 个元素的流。若流中元素
    不足 n 个,则返回一个空流。与 limit(n) 互补。
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 6, 6, 8, 9, 666, 10, 888);
    list.stream().filter(i -> i > 5).distinct().limit(4).skip(2);
    1
    2
    2)映射

    map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
    mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
    mapToInt(ToIntFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
    mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
    flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
    @Test
    public void testMapAPI() {
    List<String> strList = Arrays.asList("aba", "bdd", "cabdf");
    Stream<String> stream = strList.stream().map(String::toUpperCase);
    stream.forEach(System.out::println);
    // 效果类似于new ArrayList<String>().add(strList);
    Stream<Stream<Character>> mapStream = strList.stream().map(TestStreamAPI::filterCharacter);
    mapStream.forEach((ms) -> {
    ms.forEach(System.out::println);
    });
    // 效果类似于new ArrayList<String>().addAll(strList);
    Stream<Character> flatMapStream = strList.stream().flatMap(TestStreamAPI::filterCharacter);
    flatMapStream.forEach(System.out::println);
    }

    /**
    * 获取字符串的字符流
    *
    * @param str
    * @return
    */
    public static Stream<Character> filterCharacter(String str) {
    List<Character> list = new ArrayList<>();
    for (Character ch : str.toCharArray()) {
    list.add(ch);
    }
    return list.stream();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    3)排序

    sorted() 产生一个新流,其中按自然顺序排序
    sorted(Comparator comp) 产生一个新流,其中按比较器顺序排序
    @Test
    public void testSortAPI() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 6, 6, 8, 9, 666, 10, 888);
    list.stream().sorted().forEach(System.out::println);
    System.out.println("====================================");
    // 需要传比较器 为了简单演示且看到效果,特意将x和y交换了下
    list.stream().sorted((x, y) -> {
    return Integer.compare(y, x);
    }).forEach(System.out::println);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    4.2.3 Stream 的终止操作
    终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void。
    1)查找与匹配

    allMatch(Predicate p) 检查是否匹配所有元素
    anyMatch( Predicate p)) 检查是否至少匹配一个元素
    noneMatch(Predicate p) 检查是否没有所有元素都不匹配
    findFirst() 返回第一个元素
    count() 返回流中元素总数
    max(Comparator c ) 返回流中最大值
    min(Comparator c ) 返回流中最小值
    forEach(Consumer c ) 内部迭代
    @Test
    public void testMatchAPI() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 6, 6, 8, 9, 666, 10, 888);
    // 是否所有的值都大于6
    boolean allMatch = list.stream().allMatch(i -> i > 6);
    // 是否有值等于666
    boolean anyMatch = list.stream().anyMatch(i -> i == 666);
    // 是否没有值等于66
    boolean noneMatch = list.stream().noneMatch(i -> i == 66);
    // 取到第一个值
    Optional<Integer> findFirst = list.stream().findFirst();
    // 取到任意一个值
    Optional<Integer> findAny = list.stream().findAny();
    // 结合总数
    long count = list.stream().count();
    // 集合最大值
    Optional<Integer> max = list.stream().max((x, y) -> Integer.compare(x, y));
    // 集合中最小值
    Optional<Integer> min = list.stream().min((x, y) -> Integer.compare(x, y));
    // 遍历输出集合
    list.stream().forEach(System.out::println);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    2)归约

    reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值T。
    reduce(T iden, BinaryOperator b)可以将流中元素反复结合起来,得到一个值 Optional。
    @Test
    public void testReduce() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    Integer sum = list.stream().reduce(0, (x, y) -> x + y);
    System.out.println(sum);
    System.out.println("============================");
    // 没有给定起始数值,返回Optional
    Optional<Integer> op = list.stream().reduce((x, y) -> x + y);
    System.out.println(op.get());
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    3)收集
    collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法。Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态
    方法,可以方便地创建常见收集器实例,常用方法如下:

    List toList 把流中元素收集到List
    Set toSet 把流中元素收集到Set
    Collection toCollection 把流中元素收集到创建的集合
    Long counting 计算流中元素的个数
    Integer summingInt 对流中元素的整数属性求和
    Double averagingInt 计算流中元素Integer属性的平均值
    IntSummaryStatistics summarizingInt 收集流中Integer属性的统计值。如:平均值、最大值。最小值。
    String joining 连接流中每个字符串
    Optional maxBy 根据比较器选择最大值
    Optional minBy 根据比较器选择最小值
    reducing 从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值
    collectingAndThen 包裹另一个收集器,对其结果转换函数
    Map<K, List> groupingBy 根据某属性值对流分组,属性为K,结果为V
    Map<Boolean, List> partitioningBy 根据true或false进行分区
    @Test
    public void testCollectAPI() {
    List<Integer> arrList = Arrays.asList(1, 2, 3, 4, 5, 6, 6, 6, 8, 9, 666, 10, 888);
    // 得到list
    List<Integer> list = arrList.stream().collect(Collectors.toList());
    // 得到set
    Set<Integer> collect = arrList.stream().collect(Collectors.toSet());
    // 得到自定义集合类型
    HashSet<Integer> hashSet = arrList.stream().collect(Collectors.toCollection(HashSet::new));
    // 计算总数
    Long count = arrList.stream().collect(Collectors.counting());
    // 求和
    Integer sum = arrList.stream().collect(Collectors.summingInt(x -> x));
    // 求均值
    Double averaging = arrList.stream().collect(Collectors.averagingDouble(x -> x));
    // 求统计值
    IntSummaryStatistics summaryStatistics = arrList.stream().collect(Collectors.summarizingInt(x -> x));
    summaryStatistics.getCount();
    summaryStatistics.getMax();
    summaryStatistics.getSum();
    // 连接字符
    String join = arrList.stream().map(x -> String.valueOf(x)).collect(Collectors.joining());
    // 包裹另一个收集器,对其结果转换函数
    Integer collectingAndThen = arrList.stream()
    .collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
    // 根据属性值对流分组
    Map<String, List<Integer>> group = arrList.stream().collect(Collectors.groupingBy(x -> {
    if (x < 10) {
    return "几亿";
    } else if (x < 100) {
    return "几十亿";
    } else {
    return "好多亿";
    }
    }));
    // 分区
    Map<Boolean, List<Integer>> partition = arrList.stream().collect(Collectors.partitioningBy(x -> x > 100));
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    4.3 并行流与串行流
    并行流 就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过 parallel() 与sequential() 在并行流与顺序流之间进行切换。
    Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个
    小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总.
    采用 “工作窃取”模式(work-stealing):当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。
    相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间,提高了性能 .


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

    now() 静态方法,根据当前时间创建对象
    of() 静态方法,根据指定日期/时间创建对象
    plusDays, plusWeeks 向当前 LocalDate 对象添加几天、几周
    plusMonths, plusYears 向当前 LocalDate 对象添加几个月、几年
    minusDays, minusWeeks 从当前 LocalDate 对象减去几天、几周
    minusMonths, minusYears 从当前 LocalDate 对象减去几个月、几年
    plus, minus 添加或减少一个 Duration 或 Period
    withDayOfMonth, withDayOfYear, withMonth, withYear 将月份天数、年份天数、月份、年份修改为指定 的值 并返回新的LocalDate 对象
    getDayOfMonth 获得月份天数(1-31)
    getDayOfYear 获得年份天数(1-366)
    getDayOfWeek 获得星期几(返回一个 DayOfWeek枚举值)
    getMonth 获得月份, 返回一个 Month 枚举值
    getMonthValue 获得月份(1-12)
    getYear 获得年份
    until 获得两个日期之间的 Period 对象,或者指定 ChronoUnits 的数字
    isBefore, isAfter 比较两个 LocalDate
    isLeapYear 判断是否是闰年
    @Test
    public void testLocalDateTime() {
    LocalDateTime ldt = LocalDateTime.now();
    System.out.println(ldt);

    LocalDateTime ld2 = LocalDateTime.of(2019, 6, 6, 6, 6, 6);
    System.out.println(ld2);

    LocalDateTime ldt3 = ld2.plusYears(4);
    System.out.println(ldt3);

    LocalDateTime ldt4 = ld2.minusMonths(2);
    System.out.println(ldt4);

    System.out.println(ldt.getYear());
    System.out.println(ldt.getMonthValue());
    System.out.println(ldt.getDayOfMonth());
    System.out.println(ldt.getHour());
    System.out.println(ldt.getMinute());
    System.out.println(ldt.getSecond());
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    5.2 Instant 时间戳
    用于“时间戳”的运算。它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的描述进行运算。

    @Test
    public void testInstance() {
    Instant ins = Instant.now(); // 默认使用 UTC 时区
    System.out.println(ins);

    OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
    System.out.println(odt);

    System.out.println(ins.getNano());

    Instant ins2 = Instant.ofEpochSecond(5);
    System.out.println(ins2);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    5.3 Duration 和 Period
    Duration:用于计算两个“时间”间隔
    Period:用于计算两个“日期”间隔
    @Test
    public void testDuration() {
    Instant ins1 = Instant.now();
    try {
    Thread.sleep(9999);
    } catch (InterruptedException e) {
    }
    Instant ins2 = Instant.now();
    System.out.println("所耗费时间为:" + Duration.between(ins1, ins2));
    LocalDate ld1 = LocalDate.now();
    LocalDate ld2 = LocalDate.of(2018, 1, 1);
    Period pe = Period.between(ld2, ld1);
    System.out.println(pe.getYears());
    System.out.println(pe.getMonths());
    System.out.println(pe.getDays());
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    5.4 日期的操作
    TemporalAdjuster : 时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
    TemporalAdjusters : 该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现。
    @Test
    public void testTemporalAdjuster() {
    LocalDateTime ldt = LocalDateTime.now();
    System.out.println(ldt);

    LocalDateTime ldt2 = ldt.withDayOfMonth(10);
    System.out.println(ldt2);

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

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    5.5 解析与格式化
    java.time.format.DateTimeFormatter 类:该类提供了三种格式化方法:

    预定义的标准格式
    语言环境相关的格式
    自定义的格式
    @Test
    public void testDateTimeFormatter() {
    DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
    LocalDateTime ldt = LocalDateTime.now();
    String strDate = ldt.format(dtf);
    System.out.println(strDate);
    dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
    ldt = LocalDateTime.now();
    strDate = ldt.format(dtf);
    System.out.println(strDate);
    LocalDateTime newLdt = LocalDateTime.parse(strDate, dtf);
    System.out.println(newLdt);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    5.6 时区的处理
    Java8 中加入了对时区的支持,带时区的时间为分别为:
    ZonedDate、ZonedTime、ZonedDateTime
    其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式例如 :Asia/Shanghai 等
    ZoneId:该类中包含了所有的时区信息;getAvailableZoneIds() : 可以获取所有时区时区信息;of(id) : 用指定的时区信息获取 ZoneId 对象。

    @Test
    public void testZone() {
    Set<String> set = ZoneId.getAvailableZoneIds();
    set.forEach(System.out::println);
    LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
    System.out.println(ldt);

    ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
    System.out.println(zdt);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    5.7 与传统日期处理的转换


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

    default String name() {
    return "接口中的默认实现";
    }

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

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    7. Optional 类
    Optional 类(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
    8. 重复注解与类型注解
    Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。

    @Repeatable(RepeatableAnnotationContainer.class) // 标识可重复注解容器
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RepeatableAnnotation {

    String value() default "";
    }
    1
    2
    3
    4
    5
    6
    @Target({ ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE,
    ElementType.PARAMETER ,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RepeatableAnnotationContainer {

    RepeatableAnnotation[] value();
    }
    1
    2
    3
    4
    5
    6
    7
    @RepeatableAnnotation("Repeatable")
    @RepeatableAnnotation("Annotation")
    public void repeatableAnnotation() {

    }

    public void getName(@RepeatableAnnotation("parameter") long id) {

    }
    --------------------- 

  • 相关阅读:
    LeetCode_637.二叉树的层平均值
    LeetCode_627.变更性别
    LeetCode_617.合并二叉树
    LeetCode_595.大的国家
    LeetCode_590.N叉树的后序遍历
    LeetCode_589.N叉树的前序遍历
    LeetCode_58.最后一个单词的长度
    LeetCode_566.重塑矩阵
    LeetCode_561.数组拆分 I
    LeetCode_56.合并区间
  • 原文地址:https://www.cnblogs.com/ly570/p/10992449.html
Copyright © 2020-2023  润新知