Java 8 新特性

  • Post author:
  • Post category:java


Java 8 内容

(1)Lambda 表达式

(2)函数式接口

(3)方法引用与构造器引用

(4)Stream API

(5)接口中的默认方法与静态方法

(6)新时间日期 API

(7)其他新特性

Java 8新特性简介

(1)速度更快

(2)代码更少(增加了新的语法 Lambda 表达式)

(3)强大的 Stream API

(4)便于并行

(5)最大化减少空指针异常 Optional

1、Lambda 表达式

(1)介绍

Lambda 是一个匿名函数,Lambda 表达式可以理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使 Java的语言表达能力得到了提升。lambda表达式需要函数式接口的支持,即必须式函数式接口才能写成lambda表达式。

(2)从匿名类到 Lambda 的转换,Lambda 表达式参数的数据类型可以省略不写 Jvm可以自动进行 “类型推断”

//匿名类内部类
@Test
public void test01(){
    //匿名内部类
    Comparator<Integer> comparator = new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return Integer.compare(o1,o2);
        }

        @Override
        public boolean equals(Object obj) {
            return false;
        }
    };
    //调用
    TreeSet<Integer> set = new TreeSet<>(comparator);
}

//lambda表达式
@Test
public void test02(){
    // Lambda 表达式
    Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);
    TreeSet<Integer> set = new TreeSet<>(comparator);
}

基本语法:

- 操作符:->
- 左侧:参数列表
- 右侧:执行代码块 / Lambda 体

口诀:

写死小括号,拷贝右箭头,落地大括号
左右遇一括号省
左侧推断类型省

(2)无参数,无返回值:() -> sout (Lambda 体中只有1条语句可以省略大括号和return)

@Test
public void test02(){
    //语法糖
    Runnable runnable = () -> {
        System.out.println("Hello Lambda");
    };
}

//Lambda 体中只有1条语句可以省略大括号和return
@Test
public void test02(){
    //语法糖
    Runnable runnable = () -> System.out.println("Hello Lambda");
}

(3)有一个参数,无返回值(小括号可以省略不写)

@Test
public void test03(){
    Consumer<String> consumer = (a) -> {
        System.out.println(a);
    }
    consumer.accept("Hello Lambda");
}

//小括号可以省略不写,Lambda 体中只有1条语句可以省略大括号
@Test
public void test04(){
    Consumer<String> consumer = a -> System.out.println(a);
    consumer.accept("Hello Lambda");
}

(4)有两个及以上的参数,有返回值,并且 Lambda 体中有多条语句

@Test
public void test04(){
    Comparator<Integer> comparator = (a, b) -> {
        System.out.println("比较接口");
        return Integer.compare(a, b);
    };
}

(5)有两个及以上的参数,有返回值,并且 Lambda 体中只有1条语句 (大括号 与 return 都可以省略不写)

@Test
public void test04(){
    Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);
}

2、函数式接口

接口中只有一个抽象方法的接口 ,@FunctionalIterface可以修饰和检查函数式接口

定义一个函数式接口

@FunctionalInterface
public interface MyFun {

    Integer count(Integer a, Integer b);
}


//测试
@Test
public void test05(){
    MyFun myFun1 = (a, b) -> a + b;
    MyFun myFun2 = (a, b) -> a - b;
    MyFun myFun3 = (a, b) -> a * b;
    MyFun myFun4 = (a, b) -> a / b;
}

Java内置四大核心函数式接口:

函数式接口 参数类型 返回类型 用途
Consumer

消费型接口
T void 对类型为T的对象应用操作:void accept(T t)
Supplier

提供型接口
T 返回类型为T的对象:T get()
Function<T, R>

函数型接口
T R 对类型为T的对象应用操作,并返回结果为R类型的对象:R apply(T t)
Predicate

断言型接口
T boolean 确定类型为T的对象是否满足某约束,并返回boolean值:boolean test(T t)
//消费型接口
@Test
public void test01(){
    //Consumer
    Consumer<Integer> consumer = (x) -> System.out.println("消费型接口" + x);
    //test
    consumer.accept(100);
}

//提供型接口
@Test
public void test02(){
    List<Integer> list = new ArrayList<>();
    List<Integer> integers = Arrays.asList(1,2,3); 
    list.addAll(integers);
    //Supplier<T>
    Supplier<Integer> supplier = () -> (int)(Math.random() * 10);
    list.add(supplier.get());
    System.out.println(supplier);
    for (Integer integer : list) {
        System.out.println(integer);
    }
}

//函数型接口
@Test
public void test03(){
    //Function<T, R>
    String oldStr = "abc123456xyz";
    Function<String, String> function = (s) -> s.substring(1, s.length()-1);
    //test
    System.out.println(function.apply(oldStr));
}

//断言型接口
@Test
public void test04(){
    //Predicate<T>
    Integer age = 35;
    Predicate<Integer> predicate = (i) -> i >= 35;
    if (predicate.test(age)){
        System.out.println("你该退休了");
    } else {
        System.out.println("我觉得还OK啦");
    }
}

其他接口

3、方法引用与构造器引用

lambda体中代码逻辑已经有方法实现了,就可以直接调用方法。

(1)方法引用

语法格式:

对象 :: 实例方法

类 :: 静态方法

类 :: 实例方法

//对象::实例方法
//注意:Lambda 表达实体中调用方法的参数列表、返回类型必须和函数式接口中抽象方法保持一致
@Test
public void test01(){
    PrintStream ps = System.out;
    Consumer<String> con1 = (s) -> ps.println(s);
    con1.accept("aaa");
    //直接引用ps对象中的方法
    Consumer<String> con2 = ps::println;
    con2.accept("bbb");
}

//类::静态方法
@Test
public void test02(){
    Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y);
    System.out.println(com1.compare(1, 2));
    //引用Integer中的静态方法
    Comparator<Integer> com2 = Integer::compare;
    System.out.println(com2.compare(2, 1));
}

//类::实例方法
//使用条件:Lambda 参数列表中的第一个参数是方法的调用者,第二个参数是方法的参数时,才能使用 ClassName :: Method
@Test
public void test03(){
    BiPredicate<String, String> bp1 = (x, y) -> x.equals(y);
    System.out.println(bp1.test("a","b"));
    //直接引用类中的实例方法
    BiPredicate<String, String> bp2 = String::equals;
    System.out.println(bp2.test("c","c"));
}

(2)构造器引用

语法格式:

ClassName :: new

//注意:需要调用的构造器的参数列表要与函数时接口中抽象方法的参数列表保持一致
@Test
public void test04(){
    Supplier<Employee> sup1 = () -> new Employee();

    Supplier<Employee> sup2 = Employee::new;
}

//调用有参构造器,前面的参数决定调用哪一个构造器方法
@Test
public void test05(){
    Function<Integer,Employee> fun1= (x) -> new Employee(x);

    Supplier<Integer,Employee> fun2= Employee::new;
    Employee emp=fun2.apply(101);
}

(3)数组引用

语法: Type[] :: new

//调用有参构造器,前面的参数决定调用哪一个构造器方法
@Test
public void test05(){
    Function<Integer,String[]> fun1= (x) -> new String[x];

    Supplier<Integer,String[]> fun2= String[]::new;
    String[] strArr=fun2.apply(5);
}

4、Stream API

(1)介绍

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


流(Stream) 到底是什么呢?

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


注意:

1)Stream 自己不会存储元素。

2)Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。

3)Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

(2)Stream 的操作

(3)创建流

/**
* 创建流
*/
@Test
public void test01(){
    /**
    * 集合流,Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
    *  - Collection.stream() 穿行流
    *  - Collection.parallelStream() 并行流
    */
    List<String> list = new ArrayList<>();
    Stream<String> stream1 = list.stream();

    //map流
    Map<String,Object> map = new HashMap<>();
    Stream<Map.Entry<String,Object>> streamMap1 = map.entrySet().stream();
    Stream<Object> streamMap2 = map.values().stream();
    Stream<String> streamMap3 = map.keySet().stream();

    //数组流
    //Arrays.stream(array)
    String[] strings = new String[10];
    Stream<String> stream2 = Arrays.stream(strings);

    //Stream 静态方法
    //使用静态方法 Stream.of(...), 通过显示值创建一个流。它可以接收任意数量的参数
    Stream<Integer> stream3 = Stream.of(1, 2, 3);

    //无限流
    //迭代
    Stream<Integer> stream4 = Stream.iterate(0, (i) -> ++i+i++);
    stream4.forEach(System.out::println);

    //无限流
    //生成
    Stream.generate(() -> Math.random())
        .limit(5)
        .forEach(System.out::println);
}

(4)Stream 的中间操作

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

惰性求值

”。

1)筛选与切片

filter(Predicate p):接收 Lambda ,从流中排除某些元素

limit(long maxSize):截断流,使其元素不超过给定数量

skip(long n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与 limit(n) 互补

distinct():筛选,通过流所生成的 hashCode() 与 equals() 取除重复元素

List<Employee> emps = Arrays.asList(
    new Employee(101, "Z3", 19, 9999.99),
    new Employee(102, "L4", 20, 7777.77),
    new Employee(103, "W5", 35, 6666.66),
    new Employee(104, "Tom", 44, 1111.11),
    new Employee(105, "Jerry", 60, 4444.44)
);

@Test
public void test01(){
    emps.stream()
        .filter((x) -> x.getAge() > 35)//年龄大于35
        .limit(3) //限定取得条数最多3条,短路:当找到3条数据时,后续的查找便不再执行
        .distinct()//去除重复,需要重写hashCode() 与 equals()方法
        .skip(1)//跳过前1条数据,从第二条开始取
        .forEach(System.out::println);
}

2)映射

map(Function f):接收 Lambda ,将元素转换为其他形式或提取信息;接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素

flatMap(Function f):接收一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流

/*************map********************/
@Test
public void test02(){
    List<String> list = Arrays.asList("a", "b", "c");
    list.stream()
        .map((str) -> str.toUpperCase())
        .forEach(System.out::println);
}


/*************flatMap********************/
public Stream<Character> filterCharacter(String str){
    List<Character> list = new ArrayList<>();
    for (char c : str.toCharArray()) {
        list.add(c);
    }

    return list.stream();
}

@Test
public void test03(){
    List<String> list = Arrays.asList("a", "b", "c");
    Test02 test02 = new Test02();
    list.stream()
        .flatMap(test02::filterCharacter)
        .forEach(System.out::println);
}

3)排序

sorted():自然排序

sorted(Comparator c):定制排序

//Comparable:自然排序
@Test
public void test04(){
    List<Integer> list = Arrays.asList(1,2,3,4,5);
    list.stream()
        .sorted() //comparaTo()
        .forEach(System.out::println);
}


//Comparator:定制排序
@Test
public void test05(){
    emps.stream()
        .sorted((e1, e2) -> { //compara()
            if (e1.getAge().equals(e2.getAge())){
                return e1.getName().compareTo(e2.getName());
            } else {
                return e1.getAge().compareTo(e2.getAge());
            }
        })
        .forEach(System.out::println);
}

(5)Stream 的终止操作

1)查找与匹配

allMatch(Predicate p):检查是否匹配所有元素

anyMatch(Predicate p):检查是否至少匹配一个元素

noneMatch(Predicate p):检查是否没有匹配所有元素

findFirst():返回第一个元素

findAny():返回当前流中的任意元素

count():返回流中元素的总个数

max(Comparator c):返回流中最大值

min(Comparator c):返回流中最小值

forEach(Consumer c):内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。Stream API 使用内部迭代——它帮你把迭代做了)

public enum Status {
    FREE, BUSY, VOCATION;
}

@Test
public void test01(){
    List<Status> list = Arrays.asList(Status.FREE, Status.BUSY, Status.VOCATION);

    boolean flag1 = list.stream()
        .allMatch((s) -> s.equals(Status.BUSY));//检查是否都是BUSY的状态
    System.out.println(flag1);

    boolean flag2 = list.stream()
        .anyMatch((s) -> s.equals(Status.BUSY));
    System.out.println(flag2);

    boolean flag3 = list.stream()
        .noneMatch((s) -> s.equals(Status.BUSY));
    System.out.println(flag3);

    // 避免空指针异常
    Optional<Status> op1 = list.stream()
        .findFirst();
    // 如果Optional为空 找一个替代的对象
    Status s1 = op1.orElse(Status.BUSY);
    System.out.println(s1);

    Optional<Status> op2 = list.stream()
        .findAny();
    System.out.println(op2);

    long count = list.stream()
        .count();
    System.out.println(count);
}

2)归约 / 收集

归约:reduce(T identity, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。 返回 T

reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。 返回 Optional

/**
* Java:
*  - reduce:需提供默认值(初始值)
* Kotlin:
*  - fold:不需要默认值(初始值)
*  - reduce:需提供默认值(初始值)
*/
@Test
public void test01(){
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    Integer integer = list.stream()
        .reduce(0, (x, y) -> x + y);//0是起始值,x开始为0,y为1,执行一次后,x为上一次执行结果为1,y往后移为2....
    System.out.println(integer);

//map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它
来进行网络搜索而出名。
Optional<Double> op=employees.stream()
                 .map(Employee::getSalary)//提取Employee对象中的薪资属性
                 .reduce(Double::sum);
}


收集:collect(Collector c) 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法

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

List<Employee> emps = Arrays.asList(
    new Employee(101, "Z3", 19, 9999.99),
    new Employee(102, "L4", 20, 7777.77),
    new Employee(103, "W5", 35, 6666.66),
    new Employee(104, "Tom", 44, 1111.11),
    new Employee(105, "Jerry", 60, 4444.44)
);

//收集
@Test
public void test02(){
    //放入List
    List<String> list = emps.stream()
        .map(Employee::getName)
        .collect(Collectors.toList()); 
    list.forEach(System.out::println);
    
	//放入Set
    Set<String> set = emps.stream()
        .map(Employee::getName)
        .collect(Collectors.toSet());
    set.forEach(System.out::println);

    //放入LinkedHashSet
    LinkedHashSet<String> linkedHashSet = emps.stream()
        .map(Employee::getName)
        .collect(Collectors.toCollection(LinkedHashSet::new));
    linkedHashSet.forEach(System.out::println);
}

@Test
public void test03(){
    //总数
    Long count = emps.stream()
        .collect(Collectors.counting());
    System.out.println(count);

    //平均值
    Double avg = emps.stream()
        .collect(Collectors.averagingDouble(Employee::getSalary));
    System.out.println(avg);

    //总和
    Double sum = emps.stream()
        .collect(Collectors.summingDouble(Employee::getSalary));
    System.out.println(sum);

    //最大值
    Optional<Employee> max = emps.stream()
        .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
    System.out.println(max.get());

    //最小值
    Optional<Double> min = emps.stream()
        .map(Employee::getSalary)
        .collect(Collectors.minBy(Double::compare));
    System.out.println(min.get());
}

@Test
public void test04(){
    //分组
    Map<Integer, List<Employee>> map = emps.stream()
        .collect(Collectors.groupingBy(Employee::getId));
    System.out.println(map);

    //多级分组
    Map<Integer, Map<String, List<Employee>>> mapMap = emps.stream()
        .collect(Collectors.groupingBy(Employee::getId, Collectors.groupingBy((e) -> {
            if (e.getAge() > 35) {
                return "离开";
            } else {
                return "留下";
            }
        })));
    System.out.println(mapMap);
    
    //分区
    Map<Boolean, List<Employee>> listMap = emps.stream()
        .collect(Collectors.partitioningBy((e) -> e.getSalary() > 4321));
    System.out.println(listMap);
}

@Test
public void test05(){
    //总结
    DoubleSummaryStatistics dss = emps.stream()
        .collect(Collectors.summarizingDouble(Employee::getSalary));
    System.out.println(dss.getMax());
    System.out.println(dss.getMin());
    System.out.println(dss.getSum());
    System.out.println(dss.getCount());
    System.out.println(dss.getAverage());
    
    //连接字符串
    String str = emps.stream()
        .map(Employee::getName)
        .collect(Collectors.joining("-")); //可传入分隔符
    System.out.println(str);
}

(6)并行流

并行流:就是把一个内容分成几个数据块,并用不同的线程分别处理每个数据块的流

Java 8 中将并行进行了优化,我们可以很容易的对数据进行操作;Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与串行流之间切换

Fork / Join 框架(juc中有详解):就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个 小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总。

Java 8 并行流 / 串行流:

@Test
public void test03(){
    //串行流(单线程):切换为并行流 parallel()
    //并行流:切换为串行流 sequential()
    LongStream.rangeClosed(0, 100000000L)
        .parallel() //底层:ForkJoin
        .reduce(0, Long::sum);

}

5、Optional

定义:Optional 类 (java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在用 Optional 可以更好的表达这个概念;并且可以避免空指针异常

常用方法:

Optional.of(T t):创建一个 Optional 实例

Optional.empty(T t):创建一个空的 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

@Test
public void test01(){
    Optional<Employee> op = Optional.of(new Employee());
    //Optional<Employee> op = Optional.of(null);//一样会报空指针
    Employee employee = op.get();
}

@Test
public void test02(){
    Optional<Employee> op = Optional.empty();
    Employee employee = op.get();
}

@Test
public void test03(){
    Optional<Employee> op = Optional.ofNullable(new Employee());
    Employee employee = op.get();
}

@Test
public void test04(){
    Optional<Employee> op = Optional.ofNullable(new Employee());
    if (op.isPresent()) {
        Employee employee = op.get();
    }
}

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

(1)默认方法

Java 8中允许接口中包含具有具体实现的方法,该方法称为 “默认方法”,默认方法使用 default 关键字修饰。


接口默认方法的”类优先”原则

若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时:

1)选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。

2)接口冲突。如果一个父接口提供一个默认方法,而另一个父接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突

public interface MyFun {

    Integer fun(int n);

    default String getName(){
        return "libo";
    }

    default Integer getAge(){
        return 22;
    }
}


//假定MyFun1,MyFun2存在默认方法getName()方法
public interface MyFun implements MyFun1,MyFun2{

    @override
    public String getName(){
        return MyFun1.super.getName();
    }
}

(2) 静态方法

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

public interface MyFun {

    static void getAddr(){
        System.out.println("addr");
    }

    static String Hello(){
        return "Hello World";
    }
}

7、新时间日期 API

(1)安全问题

传统的日期格式化:

@Test
public void test01(){
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    Callable<Date> task = () -> sdf.parse("20200517");

    ExecutorService pool = Executors.newFixedThreadPool(10);

    ArrayList<Future<Date>> result = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        result.add(pool.submit(task));
    }

    for (Future<Date> future : result) {
        try {
            System.out.println(future.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
    
    pool.shutdown();
}

加锁:

public class DateFormatThreadLocal {
    private static final ThreadLocal<DateFormat> df = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

    public static Date convert(String source) throws ParseException{
        return df.get().parse(source);
    }
}

@Test
public void test02(){
    Callable<Date> task = () -> DateFormatThreadLocal.convert("20200517");

    ExecutorService pool = Executors.newFixedThreadPool(10);

    ArrayList<Future<Date>> result = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        result.add(pool.submit(task));
    }

    for (Future<Date> future : result) {
        try {
            System.out.println(future.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    pool.shutdown();
}

DateTimeFormatter:

@Test
public void test03(){
    DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;

    Callable<LocalDate> task = () -> LocalDate.parse("20200517",dtf);

    ExecutorService pool = Executors.newFixedThreadPool(10);

    ArrayList<Future<LocalDate>> result = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        result.add(pool.submit(task));
    }

    for (Future<LocalDate> future : result) {
        try {
            System.out.println(future.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    pool.shutdown();
}

(2)本地时间 / 日期

常用方法:

(3)时间戳

Instant:以 Unix 元年 1970-01-01 00:00:00 到某个时间之间的毫秒值

@Test
public void test02(){
    // 默认获取 UTC 时区 (UTC:世界协调时间)
    Instant ins1 = Instant.now();
    System.out.println(ins1);

    //带偏移量的时间日期 (如:UTC + 8)
    OffsetDateTime odt1 = ins1.atOffset(ZoneOffset.ofHours(8));
    System.out.println(odt1);

    //转换成对应的毫秒值
    long milli1 = ins1.toEpochMilli();
    System.out.println(milli1);

    //构建时间戳
    Instant ins2 = Instant.ofEpochSecond(60);
    System.out.println(ins2);
}

(4)时间 / 日期 差

Duration:计算两个时间之间的间隔

Period:计算两个日期之间的间隔

@Test
public void test03(){
    //计算两个时间之间的间隔 between
    Instant ins1 = Instant.now();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Instant ins2 = Instant.now();
    Duration dura1 = Duration.between(ins1, ins2);
    System.out.println(dura1.getSeconds());
    System.out.println(dura1.toMillis());
}

@Test
public void test04(){
    LocalDate ld1 = LocalDate.of(2016, 9, 1);
    LocalDate ld2 = LocalDate.now();
    Period period = Period.between(ld1, ld2);  // ISO 标准
    System.out.println(period.getYears());
    System.out.println(period.toTotalMonths());
}

(5)时间校正器

@Test
public void test01(){
    //TemporalAdjusters:时间校正器
    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((ta) -> {
        LocalDateTime ldt4 = (LocalDateTime) ta;
        DayOfWeek dow1 = ldt4.getDayOfWeek();
        if (dow1.equals(DayOfWeek.FRIDAY)) {
            return ldt4.plusDays(3);
        } else if (dow1.equals(DayOfWeek.SATURDAY)) {
            return ldt4.plusDays(2);
        } else {
            return ldt4.plusDays(1);
        }
    });
    System.out.println(ldt5);
}

(6)格式化

DateTimeFormatter:格式化时间 / 日期

@Test
public void test01(){
    //默认格式化
    DateTimeFormatter dtf1 = DateTimeFormatter.ISO_DATE_TIME;
    LocalDateTime ldt1 = LocalDateTime.now();
    String str1 = ldt1.format(dtf1);
    System.out.println(str1);

    //自定义格式化 ofPattern
    DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    LocalDateTime ldt2 = LocalDateTime.now();
    String str2 = ldt2.format(dtf2);
    System.out.println(str2);

    //解析
    LocalDateTime newDate = ldt1.parse(str1, dtf1);
    System.out.println(newDate);
}

(7)时区

ZonedDate

ZonedTime

ZonedDateTime

@Test
public void test02(){
    //查看支持的时区
    Set<String> set = ZoneId.getAvailableZoneIds();
    set.forEach(System.out::println);

    //指定时区
    LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("Europe/Tallinn"));
    System.out.println(ldt1);

    //在已构建好的日期时间上指定时区
    LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("Europe/Tallinn"));
    ZonedDateTime zdt1 = ldt2.atZone(ZoneId.of("Europe/Tallinn"));
    System.out.println(zdt1);
}

8、注解:重复注解与类型注解

Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。



版权声明:本文为qq_24757635原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。