java8新特性


参考链接

https://www.runoob.com/java/java8-new-features.html

1、默认方法

Java 8 新增了接口的默认方法。

简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。

我们只需在方法名前面加个 default 关键字即可实现默认方法。

接口可以有多个默认方法

为什么要有这个特性?

首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的 java 8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。

默认方式书写

public interface Vehicle {
   default void print(){
      System.out.println("我是一辆车!");
   }
}

多个相同的默认方法,解决方法

一个接口有默认方法,考虑这样的情况,一个类实现了多个接口,且这些接口有相同的默认方法,

第一个解决方案是创建自己的默认方法,来覆盖重写接口的默认方法:

第二种解决方案可以使用 super 来调用指定接口的默认方法:

public interface Vehicle {
   default void print(){
      System.out.println("我是一辆车!");
   }
}

public interface FourWheeler {
   default void print(){
      System.out.println("我是一辆四轮车!");
   }
}

//方法一,重写方法
public class Car implements Vehicle, FourWheeler {
   default void print(){
      System.out.println("我是一辆四轮汽车!");
   }
}

//方法二,直接调用具体方法
public class Car implements Vehicle, FourWheeler {
   public void print(){
      Vehicle.super.print();
   }
}

静态默认方法

Java 8 的另一个特性是接口可以声明(并且可以提供实现)静态方法。例如:

public interface Vehicle {
   default void print(){
      System.out.println("我是一辆车!");
   }
    // 静态方法
   static void blowHorn(){
      System.out.println("按喇叭!!!");
   }
}

2、Lambda 表达式

Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。

以下是lambda表达式的重要特征:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

简单例子

// 1. 不需要参数,返回值为 5  
() -> 5  

// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  

// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  

// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  

// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

变量作用域

lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。

lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义),否则会编译错误。

public class Java8Tester {

   final static String s1 = "Hello! ";

   public static void main(String args[]){

      String s2 = "world"

      GreetingService greetService1 = message -> 
              System.out.println(s1 + s2 + message);
      greetService1.sayMessage("Runoob");

      //s2 = "aaa";     //编译错误。
   }

   interface GreetingService {
      void sayMessage(String message);
   }
}

在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

3、方法的引用

定义:Lambda 表达式体中的内容已有方法实现,则我们可以使用“方法引用”,简而言之,就是lambda的简化

方法引用通过方法的名字来指向一个方法。

方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

方法引用使用一对冒号 ::

类型 语法 对应的Lambda表达式
静态方法引用 类名::静态方法 (args) -> 类名.staticMethod(args)
实例方法引用 对象名::实例方法 (args) -> inst.instMethod(args)
对象方法引用 类名::实例方法 (inst,args) -> 类名.instMethod(args)
构建方法引用 类名::构造方法 (args) -> new 类名(args)

对象::实例方法

@Test
public void test01(){
    PrintStream ps = System.out;
    Consumer<String> con1 = (s) -> ps.println(s);
    con1.accept("aaa");

    Consumer<String> con2 = ps::println;
    con2.accept("bbb");
}

注意:Lambda 表达实体中调用方法的参数列表、返回类型必须和函数式接口中抽象方法保持一致

类::静态方法

@Test
public void test02(){
    Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y);
    System.out.println(com1.compare(1, 2));

    Comparator<Integer> com2 = Integer::compare;
    System.out.println(com2.compare(2, 1));
}

类::实例方法

@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"));
}

条件:Lambda 参数列表中的第一个参数是方法的调用者,第二个参数是方法的参数时,才能使用 ClassName :: Method

构造器引用

@Test
public void test04(){
    Supplier<List> sup1 = () -> new ArrayList();

    Supplier<List> sup2 = ArrayList::new;
}

注意:需要调用的构造器的参数列表要与函数时接口中抽象方法的参数列表保持一致

4、函数式接口

jdk1.8新增java.util.function

(1)函数式接口

/**
 * Function 函数型接口, 有一个输入参数,有一个输出
 * 只要是 函数型接口 可以 用 lambda表达式简化
 */
public class Demo01 {
    public static void main(String[] args) {
//        Function<String,String> function = new Function<String,String>() {
//            @Override
//            public String apply(String str) {
//                return str;
//            }
//        };

        Function<String,String> function = str->{return str;};

        System.out.println(function.apply("asd"));
    }
}

(2)断定型接口

/**
 * 断定型接口:有一个输入参数,返回值只能是 布尔值!
 */
public class Demo02 {
    public static void main(String[] args) {
        // 判断字符串是否为空
//        Predicate<String> predicate = new Predicate<String>(){
//            @Override
//            public boolean test(String str) {
//                return str.isEmpty();
//            }
//        };

        Predicate<String> predicate = (str)->{return str.isEmpty(); };
        System.out.println(predicate.test(""));

    }
}

(3)消费型接口

/**
 * Consumer 消费型接口: 只有输入,没有返回值
 */
public class Demo03 {
    public static void main(String[] args) {
//        Consumer<String> consumer = new Consumer<String>() {
//            @Override
//            public void accept(String str) {
//                System.out.println(str);
//            }
//        };
        Consumer<String> consumer = (str)->{System.out.println(str);};
        consumer.accept("sdadasd");

    }
}

(4)供给型接口

/**
 * Supplier 供给型接口 没有参数,只有返回值
 */
public class Demo04 {
    public static void main(String[] args) {
//        Supplier supplier = new Supplier<Integer>() {
//            @Override
//            public Integer get() {
//                System.out.println("get()");
//                return 1024;
//            }
//        };

        Supplier supplier = ()->{ return 1024; };
        System.out.println(supplier.get());
    }
}

5、Stream

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

/**
 * 题目要求:一分钟内完成此题,只能用一行代码实现!
 * 现在有5个用户!筛选:
 * 1、ID 必须是偶数
 * 2、年龄必须大于23岁
 * 3、用户名转为大写字母
 * 4、用户名字母倒着排序
 * 5、只输出一个用户!
 */
public class Test {
    public static void main(String[] args) {
        User u1 = new User(1,"a",21);
        User u2 = new User(2,"b",22);
        User u3 = new User(3,"c",23);
        User u4 = new User(4,"d",24);
        User u5 = new User(6,"e",25);
        // 集合就是存储
        List<User> list = Arrays.asList(u1, u2, u3, u4, u5);

        // 计算交给Stream流
        // lambda表达式、链式编程、函数式接口、Stream流式计算
        list.stream()
                .filter(u->{return u.getId()%2==0;})
                .filter(u->{return u.getAge()>23;})
                .map(u->{return u.getName().toUpperCase();})
                .sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
                .limit(1)
                .forEach(System.out::println);

        //System.out::println打印的是传入的参数,也就是Customer的参数泛型T,这个T就是从list的流对象获取的T,最终都是List对象创建时指定的泛型
    }
}

执行流程

  1. 创建流
  2. 中间操作
  3. 终止操作
/**
     * stream 的执行流程
     * 多个中间操作形成一个流水线,除非流水线上触发终止操作否则中间操作不会执行任何处理
     * 而是在终止操作时,一次性全部处理
     * 这个性质称为“惰性求值”
     */
@Test
public void test2(){
    List<User> users = this.getUsers();

    users.stream().filter((x)->{
        System.out.println(x.getName()+x.getAge());
        return true;
    }).forEach(System.out::println);
}

(1)创建流

/**
     * 创建 Stream 的4种方式
     */
@Test
public void test1(){
    // 1、可以通过 Collection 集合的 stream() 或 parallelStream()创建
    ArrayList<String> list = new ArrayList<>();
    Stream<String> stream1 = list.stream();
    Stream<String> stream2 = list.parallelStream();

    // 2、通过 Arrays 的静态方法 stream() 创建流
    String[] array = new String[]{"1","2","3"};
    Stream<String> stream3 = Arrays.stream(array);

    // 3、通过Stream 的静态方法 of
    Stream<String> stream4 = Stream.of("a", "b");

    // 4、创建无限流
    // 4.1 迭代
    Stream<Integer> stream5 = Stream.iterate(0, (s) -> s + 2);
    stream5.limit(10).forEach(System.out::println);
    // 4.2 生成
    Stream.generate(()->Math.random())
        .limit(10)
        .forEach(System.out::println);
}

(2)中间操作

① 筛选与切片

中间操作:

filter:接收 Lambda ,从流中排除某些元素
limit:截断流,使其元素不超过给定数量
skip(n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与 limit(n) 互补
distinct:筛选,通过流所生成的 hashCode()equals() 取除重复元素,操作对象需重写这俩方法
/**
* 中间操作:筛选与切片
*/
@Test
public void test3(){
    List<User> users = this.getUsers();
    users.stream()
        .filter((s)->s.getAge()>15) //通过断言式接口,对数据进行过滤
        .forEach(System.out::println);

    System.out.println("===========================");
    // limit 存在短路操作 ,即当只执行到指定数量,即使是limit前的中间操作也不会执行
    users.stream()
        .filter((x)->{  //由于limit短路,该操作只执行2次
            System.out.println(x.getName());
            return x.getAge()>15;
        })
        .limit(2)   //截断数据
        .forEach(System.out::println);

    System.out.println("===========================");
    users.stream()
        .skip(2)
        .forEach(System.out::println);

    System.out.println("===========================");
    users.stream()
        .distinct()
        .forEach(System.out::println);

}

② 映射

  • map:接收 Lambda ,将元素转换为其他形式或提取信息;接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
  • flatMap:接收一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流
@Test
public void test4(){
    List<User> users = this.getUsers();
    users.stream().map((x)->x.getName()).forEach(System.out::println);
}
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);
}

③ 排序

  • sorted():自然排序—–Comparable
  • sorted(Comparator c):定制排序—–Comparator
@Test
public void test04(){
    List<Integer> list = Arrays.asList(1,2,3,4,5);
    list.stream()
        .sorted() //comparaTo()
        .forEach(System.out::println);
}
@Test
public void test5(){
    // Collator 类是用来执行区分语言环境这里使用CHINA
    // 该接口可用于对中文进行排序
    Comparator cmp = Collator.getInstance(java.util.Locale.CHINA);

    List<User> users = this.getUsers();
    users.stream()
        .sorted((x,y)->{

            // 先按年龄进行排序,年龄相同则按姓名进行排序
            if (Objects.equals(x.getAge(), y.getAge())){
                return cmp.compare(x.getName(),y.getName());
            }else{
                return x.getAge().compareTo(y.getAge());
            }

        }).forEach(System.out::println);
}

(3)终止操作

① 查找/匹配

  • allMatch:检查是否集合中所有元素符合相应条件
  • anyMatch:检查是否集合中至少一个元素符合相应条件
  • noneMatch:检查是否集合中所有元素都不符合相应条件
  • findFirst:返回第一个元素
  • findAny:返回当前流中的任意元素(随机)
  • count:返回流中元素的总个数
  • max:返回流中最大值
  • min:返回流中最小值

判断匹配元素,流返回布尔值

public enum Status {
    FREE, BUSY, VOCATION;
}

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

    // 所有元素匹配才返回true
    boolean flag1 = list.stream()
        .allMatch((s) -> s.equals(Status.BUSY));
    System.out.println(flag1);

    // 至少一个元素匹配返回true
    boolean flag2 = list.stream()
        .anyMatch((s) -> s.equals(Status.BUSY));
    System.out.println(flag2);

    // 所有元素不匹配才返回true
    boolean flag3 = list.stream()
        .noneMatch((s) -> s.equals(Status.BUSY));
    System.out.println(flag3);

    // 随机返回一个元素
    Optional<Status> op2 = list.stream()
        .findAny();
    System.out.println(op2);

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

查找一个元素,返回元素对象的容器

/**
* findAny:返回当前流中的任意元素(随机)
* parallel: 返回平行的等效流。 将流的执行由串行变为并行
*/
@Test
public void test1(){
    List<User> users = this.getUsers();

    // Optional容器接收
    Optional<User> userOptional = users.stream().parallel()
        .filter((x)->x.getAge()>20)
        .findAny();
    // 判断 Optional 容器中是否有值,
    // 如果有则执行参数对应的消费型接口内容,接口的入参为对应的实例对象User
    userOptional.ifPresent(System.out::println);

    //-----------------------------------------------------
    User user2 = users.stream().parallel()
        .filter((x)->x.getAge()>20)
        .findAny()
        .orElse(new User("张三",20));//如果流中没有值,则返回指定对象
    System.out.println(user2);

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


    //--------------------------------------------------------
    Optional<User> first = users.stream().findFirst();
    System.out.println(first);
}

② 收集

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

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

另存到集合

@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<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 test10(){
    String str = employees.stream()
        .map(Employee::getName)
        // 参数:分隔符、开头字符串、结尾字符串
        .collect(Collectors.joining(",","###","###"));
    System.out.println(str);
}
// 分组
@Test
public void test9(){
    DoubleSummaryStatistics dss = employees.stream()
        .collect(Collectors.summarizingDouble(Employee::getSalary));
    System.out.println(dss.getSum());
    System.out.println(dss.getAverage());
    System.out.println(dss.getMax());
}

③ 归约

  • 归约:reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值
// 计算集合中所有元素的和
@Test    
public void test3(){
    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);
}

// 将所有人的姓名字符串拼接,并以“姓名列表:”开头
@Test
public void test3(){
    List<User> users = this.getUsers();

    String s = users.stream()
        .map(User::getName)
        .reduce("姓名列表:", (x, y) -> x + y);

    System.out.println(s);
}

6、Fork/Join 并行流

多线程高级JUC 第七节

7、Optional 类

Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

Optional 类的引入很好的解决空指针异常。

常用方法:

  1. Optional.of(T t):创建一个 Optional 实例
  2. Optional.empty(T t):创建一个空的 Optional 实例
  3. Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则空实例
  4. isPresent():判断是否包含某值
  5. orElse(T t):如果调用对象包含值,返回该值,否则返回 t
  6. orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回 s 获取的值
  7. map(Function f):如果有值对其处理,并返回处理后的 Optional,否则返回 Optional.empty()
  8. flatmap(Function mapper):与 map 相似,要求返回值必须是 Optional
@Test
public void test01(){
    // 创建实例
    Optional<Employee> op = Optional.of(new Employee());
    Employee employee = op.get();
}

@Test
public void test02(){
    // 创建一个空的 Optional 实例
    Optional<Employee> op = Optional.empty();
    Employee employee = op.get();
}

@Test
public void test03(){
    // 若 传入对象 不为 null,创建 Optional 实例,否则空实例
    Optional<Employee> op = Optional.ofNullable(new Employee());
    Employee employee = op.get();
}

@Test
public void test03(){
    Optional<Employee> op = Optional.ofNullable(new Employee());
    //判断是否包含某值
    if (op.isPresent()) {
        Employee employee = op.get();
    }
}

8、本地化日期时间 API

Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。

iso标准:

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

常用方法

方法名 返回值类型 解释
now( ) LocalDateTime 从默认时区的系统时钟获取当前日期
of(int year, int month, int dayOfMonth, int hour, int minute, int second) LocalDateTime 从年,月,日,小时,分钟和秒获得 LocalDateTime的实例,将纳秒设置为零
plus(long amountToAdd, TemporalUnit unit) LocalDateTime 返回此日期时间的副本,并添加指定的数量
get(TemporalField field) int 从此日期时间获取指定字段的值为 int

1、获取\加减日期时间

LocalDateTime:存储日期时间

LocalDate :存储日期

LocalTime :存储时间

LocalDate / LocalTime 与LocalDateTime类似,不再举例

@Test
public void test01(){
    //获取当前时间日期 now
    LocalDateTime ldt1 = LocalDateTime.now();
    System.out.println(ldt1);

    //指定时间日期 of
    LocalDateTime ldt2 = LocalDateTime.of(2020, 05, 17, 16, 24, 33);
    System.out.println(ldt2);

    //加 plus
    LocalDateTime ldt3 = ldt2.plusYears(2);
    System.out.println(ldt3);

    //减 minus
    LocalDateTime ldt4 = ldt2.minusMonths(3);
    System.out.println(ldt4);

    //获取指定的你年月日时分秒... get
    System.out.println(ldt2.getDayOfYear());
    System.out.println(ldt2.getHour());
    System.out.println(ldt2.getSecond());
}

2、时间戳

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);
}

3、时间/日期差

  • Duration:计算两个时间之间的间隔
  • Period:计算两个日期之间的间隔
@Test
public void test03(){

    Instant ins1 = Instant.now();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Instant ins2 = Instant.now();

    //计算两个时间之间的间隔 between
    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();

    //计算两个时间之间的间隔 between
    Period period = Period.between(ld1, ld2);  // ISO 标准
    System.out.println(period.getYears());
    System.out.println(period.toTotalMonths());
}

4、时间校正器

TemporalAdjuster:时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。

TemporalAdjusters :该类通过静态方法提供了大量的常用TemporalAdjuster的实现。

// 获取下一个周日
LocalDate date = LocalDate.now().with(
    TemporalAdjusters.next(DayOfWeek.SUNDAY)
);
@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);
}

5、格式化

  • 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);
}

6、时区

  • 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);
}

转换

@Test
public void test03(){
    // Date 转 LocalDateTime 
    Date date = new Date();
    Instant instant = date.toInstant();
    ZoneId zoneId = ZoneId.systemDefault();
    LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime();

    // LocalDateTime 转 Date
    LocalDateTime localDateTime = LocalDateTime.now();
    ZoneId zoneId = ZoneId.systemDefault();
    ZonedDateTime zdt = localDateTime.atZone(zoneId);
    Date date = Date.from(zdt.toInstant());

    // 原则:利用 时间戳Instant
}

7、与Date( )的转化

(1)Date转换成LocalDate

public static LocalDate date2LocalDate(Date date) {
    if(null == date) {
        return null;
    }
    return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}

(2)LocalDate转换成Date

public static Date localDate2Date(LocalDate localDate) {
    if(null == localDate) {
        return null;
    }
    ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
    return Date.from(zonedDateTime.toInstant());
}

(3)LocalDateTime转换成Date

public static Date localDateTime2Date(LocalDateTime localDateTime) {
    return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}

(4)LocalDate格式化

public static String formatDate(Date date) {
    LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    return localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}

  目录