jdk8新特性的使用


1、Lambda表达式

lambda简介

Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。

JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。

对接口的要求

虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法

@FunctionalInterface是一个标志注解,被修饰的接口中的抽象方法只能有一个。 这个注解往往会和 lambda 表达式一起出现。

lambda初体验

在传统的写法中,我们创建一个线程通常的写法是:

new Thread(new Runnable() {
           @Override
           public void run() {
               //业务代码
           }
}).start();

使用lambda表达式时写法是:

new Thread(()->{
           //业务代码
}).start();

上下代码两者实现的功能是完全一样的,由此可见lanbda表达式的使用会简化我们的代码,使代码更加优雅。

lambda语法

lambda表达式由组成三部分:

  ()  -> { }
// () 为参数列表    ->  为运算符(读作goes to)		{ } 为方法体

由此可见lambda表达式简化了方法的修饰符,返回值,方法名

注:

1、所有参数都可以不写参数类型

2、只有一个参数时可省略(),方法体只有一条语句时可以省略{}

Lambda 表达式常用示例

  • lambda 表达式引用方法

有时候我们不是必须要自己重写某个匿名内部类的方法,我们可以可以利用 lambda表达式的接口快速指向一个已经被实现的方法。

语法

​ 方法归属者::方法名 静态方法的归属者为类名,普通方法归属者为对象

public class Exe1 {
    public static void main(String[] args) {
        ReturnOneParam lambda1 = a -> doubleNum(a);
        System.out.println(lambda1.method(3));

        //lambda2 引用了已经实现的 doubleNum 方法  此种用法在mybatisplus中用来构建sql语句时常用
        ReturnOneParam lambda2 = Exe1::doubleNum;
        System.out.println(lambda2.method(3));

        Exe1 exe = new Exe1();

        //lambda4 引用了已经实现的 addTwo 方法
        ReturnOneParam lambda4 = exe::addTwo;
        System.out.println(lambda4.method(2));
    }

    /**
     * 要求
     * 1.参数数量和类型要与接口中定义的一致
     * 2.返回值类型要与接口中定义的一致
     */
    public static int doubleNum(int a) {
        return a * 2;
    }

    public int addTwo(int a) {
        return a + 2;
    }
}
  • 构造方法的引用

一般我们需要声明接口,该接口作为对象的生成器,通过 类名::new 的方式来实例化对象,然后调用方法返回对象。

interface ItemCreatorBlankConstruct {
    Item getItem();
}
interface ItemCreatorParamContruct {
    Item getItem(int id, String name, double price);
}

public class Exe2 {
    public static void main(String[] args) {
        ItemCreatorBlankConstruct creator = () -> new Item();
        Item item = creator.getItem();

        ItemCreatorBlankConstruct creator2 = Item::new;
        Item item2 = creator2.getItem();

        ItemCreatorParamContruct creator3 = Item::new;
        Item item3 = creator3.getItem(112, "鼠标", 135.99);
    }
}
  • lambda 表达式创建线程

我们以往都是通过创建 Thread 对象,然后通过匿名内部类重写 run() 方法,一提到匿名内部类我们就应该想到可以使用 lambda 表达式来简化线程的创建过程。

Thread t = new Thread(() -> {
    for (int i = 0; i < 10; i++) {
      System.out.println(2 + ":" + i);
    }
  });
t.start();
  • 遍历集合

我们可以调用集合的 public void forEach(Consumer<? super E> action) 方法,通过 lambda 表达式的方式遍历集合中的元素。以下是 Consumer 接口的方法以及遍历集合的操作。Consumer 接口是 jdk 为我们提供的一个函数式接口。

ArrayList<Integer> list = new ArrayList<>();

Collections.addAll(list, 1,2,3,4,5);

//lambda表达式 方法引用
list.forEach(System.out::println);

list.forEach(element -> {
    if (element % 2 == 0) {
        System.out.println(element);
    }
});
  • 删除集合中的某个元素

我们通过public boolean removeIf(Predicate<? super E> filter)方法来删除集合中的某个元素,Predicate 也是 jdk 为我们提供的一个函数式接口,可以简化程序的编写。

ArrayList<Item> items = new ArrayList<>();
    items.add(new Item(11, "小牙刷", 12.05 ));
    items.add(new Item(5, "日本马桶盖", 999.05 ));
    items.add(new Item(7, "格力空调", 888.88 ));
    items.add(new Item(17, "肥皂", 2.00 ));	
    items.add(new Item(9, "冰箱", 4200.00 ));

items.removeIf(ele -> ele.getId() == 7);

//通过 foreach 遍历,查看是否已经删除
items.forEach(System.out::println);
  • 集合内元素的排序

在以前我们若要为集合内的元素排序,就必须调用 sort 方法,传入比较器匿名内部类重写 compare 方法,我们现在可以使用 lambda 表达式来简化代码。

ArrayList<Item> list = new ArrayList<>();
list.add(new Item(13, "背心", 7.80));
list.add(new Item(11, "半袖", 37.80));
list.add(new Item(14, "风衣", 139.80));
list.add(new Item(12, "秋裤", 55.33));

/*
        list.sort(new Comparator<Item>() {
            @Override
            public int compare(Item o1, Item o2) {
                return o1.getId()  - o2.getId();
            }
        });
        */

list.sort((o1, o2) -> o1.getId() - o2.getId());

System.out.println(list);

Lambda 表达式中的闭包问题

这个问题我们在匿名内部类中也会存在,如果我们把注释放开会报错,告诉我 num 值是 final 不能被改变。这里我们虽然没有标识 num 类型为 final,但是在编译期间虚拟机会帮我们加上 final 修饰关键字。

import java.util.function.Consumer;
public class Main {
    public static void main(String[] args) {

        int num = 10;

        Consumer<String> consumer = ele -> {
            System.out.println(num);
        };

        //num = num + 2;
        consumer.accept("hello");
    }
}

2、接口增强

在1.8之前接口中只能有:静态常量,抽象方法。

在1.8之后接口中可以有:静态常量,抽象方法,默认方法,静态方法

默认方法

语法格式

interface 接口名{
    修饰符 default 返回值 方法名(){
        //方法体
    }
}

使用方式

​ 1、接口的实现类直接调用接口的默认方法

​ 2、子类重写接口的默认方法

静态方法

interface 接口名{
    修饰符 static 返回值 方法名(){
        //方法体
    }
}

使用方式:只能通过 接口名.方法名

注:默认方法可以被继承和重新,而静态方法都不能

3、函数式接口

由来:使用Lambda表达式的前提是有函数式接口,而Lambda表达式使用时不关心接口名,抽象方法名。只关心抽象方法的参数列表和返回值类型,因此为了更加方便的使用Lambda表达式,JDK提供了大量常用的函数式接口(java.util.function包下)

Supplier

无参有返回值,用于生产数据

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

使用:

public static void main(String[] args) {
   fun1(()->{
       int[] arr={10,22,78,3,56,43,29,75};
       Arrays.sort(arr);
       return arr[arr.length-1];
   });
}

private static void fun1(Supplier<Integer> supplier){
    Integer max = supplier.get();
    System.err.println("max = "+max);
}

Consumer

有参无返回值,用于消费数据

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
}

使用:

public static void main(String[] args) {
      fun1( msg->{
          System.err.println(msg+" ---转换为大写--- "+msg.toUpperCase());
      });
   }

   private static void fun1(Consumer<String> consumer){
       consumer.accept("consumer accept");
   }

默认方法andThen使用:(进行组合操作)

public static void main(String[] args) {
     fun1( msg->{
         System.err.println(msg+" ---转换为大写--- "+msg.toUpperCase());
     },msg2->{
         System.err.println(msg2+" ---转换成小写--- "+msg2.toLowerCase());
     });
  }

  private static void fun1(Consumer<String> c1,Consumer<String> c2){
      String str = "consumer accept";
      c1.andThen(c2).accept(str);  //c1先执行,执行完毕再执行c2
  }

Function

有参有返回值,根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件

public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
}

使用:

public static void main(String[] args) {
      fun1( msg->{
          return Integer.parseInt(msg);
      });
   }

   private static void fun1(Function<String,Integer> function){
       System.err.println(function.apply("123345"));
   }

默认方法andThen使用:(进行组合操作)同上

Predicate

@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
}

4、Stream流

什么是Stream?

Java8 中,Collection 新增了两个流方法,分别是 Stream() 和 parallelStream()

Java8 中添加了一个新的接口类 Stream,相当于高级版的 Iterator,它可以通过 Lambda 表达式对集合进行大批量数据操作,或 者各种非常便利、高效的聚合数据操作。

Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream API能让我们快速完成很多复杂的操作,如筛选、切片、映射、查找、去重、统计、匹配和规约。

为什么要使用 Stream?

在 Java8 之前,我们通常是通过 for 循环或者 Iterator 迭代来重新排序合并数据,又或者通过重新定义 Collections.sorts 的 Comparator 方法来实现,这两种方式对于大数据量系统来说,效率并不是很理想。Stream 的聚合操作与数据库 SQL 的聚合操作 sorted、filter、map 等类似。我们在应用层就可以高效地实现类似数据库 SQL 的 聚合操作了,而在数据操作方面,Stream 不仅可以通过串行的方式实现数据操作,还可以通过并行的方式处理大批量数据,提高数据 的处理效率。

Stream流的获取方式

根据Collection

Collection中加入了default方法获取Stream流

 List<String> list = new ArrayList<>();
 list.stream();

 Set<String> set = new HashSet<>();
 set.stream();

Map<String,Object> map=new HashMap<>();
  map.keySet().stream();
  map.values().stream();
  map.entrySet().stream();

通过Stream的of方法

String[] s1={"aa","bb","cc"};
Stream.of(s1);

Stream的常用方法

方法名 方法作用 返回值类型 方法种类 是否支持链式调用
count 统计个数 long 终结 否
forEach 遍历 void 终结 否
filter 过滤 Stream 函数拼接 是
limit 取用前几个 Stream 函数拼接 是
skip 跳过几个 Stream 函数拼接 是
map 映射 Stream 函数拼接 是
concat 组合 Stream 函数拼接 是

终结方法:返回值类型不再是Stream接口本身类型的方法,例如:forEach方法和count方法
非终结方法/延迟方法:返回值类型仍然是Stream接口自身类型的方法,除了终结方法都是延迟方法

注:

  1. Stream只能操作一次
  2. Stream返回的是新的流
  3. Stream不调用终结方法,中间的操作不会执行
forEach

​ void forEach(Consumer<? super T> action):逐一处理流中的元素(遍历流)
​ 参数 Consumer<? super T> action : 函数式接口, 只有一个抽象方法:void accept(T t);
​ 注意:
​ 1.此方法并不保证元素的逐一消费动作在流中是有序执行的
​ 2.Consumer 是一个消费接口(可以获取流中的元素进行遍历操作,输出出去),可以使用lambda表达式

public class Tests_forEach {
    public static void main(String[] args) {
    List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("张三四");
        list.add("王五");
        list.add("赵六");
        list.add("张三三");
        list.add("周八");
        // 函数模型:获取流 --> 逐一消费流中的元素
         // 函数模型:获取流 --> 逐一消费流中的元素
        list.stream().forEach(System.err::println);
        /*
        * 虽然方法名称叫做【forEach】,但是与for循环中的【for-each】昵称不同,该方法并不保证元素的逐一消费动作在流中是被有序执行的
        * */
        System.out.println("==========================");
        // parallelStream()方法可以得到并行流
        list.parallelStream().forEach((String name)->{
            System.out.println(name);
        });
    }
}
count

​ long count(); 统计流中的元素,返回long类型数据

public class Tests_count {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("张三四");
        list.add("王五");
        list.add("赵六");
        list.add("张三三");
        list.add("周八");
        long count = list.stream().count();
        System.out.println("流中的属于个数:"+count);
    }
}
filter

Stream filter(Predicate<? super T> predicate); 过滤出满足条件的元素,将一个流转换为一个子集流
参数Predicate:函数式接口,抽象方法:boolean test(T t);
Predicate接口:是一个判断接口

public class Tests_filter {
    public static void main(String[] args) {
        // 获取stream流
        Stream<String> stream = Stream.of("张三", "张三四", "王五", "赵六", "张三三", "周八");
        // 需求:过滤处
        stream.filter((String name)->{
            return name.startsWith("张");
        }).forEach((String name)->{
            System.out.println(name);
        });
    }
}
limit

Stream 对流进行截取处理,截取前n个元素
注意: 参数是一个long类型,如果流的但钱长度大于参数则进行截取;否则不进行操作

public class Tests_limit {
    public static void main(String[] args) {
        // 获取stream流
        Stream<String> stream = Stream.of("张三", "张三四", "王五", "赵六", "张三三", "周八");
        // 需求:保留前3个元素
        stream.limit(3).forEach((String name) -> {
            System.out.println(name);
        });
        System.out.println("===========================");
        // 如果流的获取长度 大于 流当前的长度,则不操作
 
        stream.limit(10).forEach(name-> System.out.println(name)); // 异常: stream has already
                                                // been operated upon or closed 流已被操作或关闭
 
        Stream<String> stream1 = Stream.of("张三", "张三四", "王五", "赵六", "张三三", "周八");
        stream1.limit(10).forEach(name-> System.out.println(name));
    }
}
skip

Stream skip(long n); 跳过前几个元素
注意:如果流的当前长度大于 n,则跳过前n个,否则将会得到一个长度为0的空流

public class Tests_skip {
    public static void main(String[] args) {
        // 获取stream流
        Stream<String> stream = Stream.of("张三", "张三四", "王五", "赵六", "张三三", "周八");
        // 需求:跳过前3个
        stream.skip(3).forEach(name-> System.out.println(name));
    }
}
map

在对集合进行操作的时候,我们经常会从某些对象中选择性的提取某些元素的值,就像编写sql一样,指定获取表 中特定的数据列

Stream map(Function<? super T, ? extends R> mapper);
参数 Function<T,R>:函数式接口,抽象方法: R apply(T t);
Function<T,R>:其实就是一个类型转换接口(T和R的类型可以一致,也可以不一致)

案例:获取所有学生的姓名,并形成一个新的集合

public class MapDemo {
    public static void main(String[] args) {
        //获取所有学生的姓名,并形成一个新的集合
        ArrayList<Student> students = new ArrayList<>();
        students.add(new Student(1,19,"张三","M",true));
        students.add(new Student(1,18,"李四","M",false));
        students.add(new Student(1,21,"王五","F",true));
        students.add(new Student(1,20,"赵六","F",false));

        List<String> collect = students.stream().map(Student::getName).collect(Collectors.toList());
        collect.forEach(s -> System.out.print(s + " "));
    }
}

//结果:张三 李四 王五 赵六
sorted

sort可以对集合中的所有元素进行排序

List<Integer> sortLists = new ArrayList<>();
sortLists.add(1);
sortLists.add(4);
sortLists.add(6);
sortLists.add(3);
sortLists.add(2);
List<Integer> afterSortLists = sortLists.stream().sorted((In1,In2)->
       In1-In2).collect(Collectors.toList());
concat

如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat:

static Stream concat(Stream<? extends T> a, Stream<? extends T> b)
注:这是一个静态方法,与 java.lang.String 当中的 concat 方法是不同的。

public static void main(String[] args) {
    Stream<String> original1 = Stream.of("Java", "C", "Python");
    Stream<String> original2 = Stream.of("Hadoop", "Spark");
    Stream<String> result = Stream.concat(original1, original2);
    result.forEach(System.out::println);
}
distinct

去除重复元素,这个方法是通过类的 equals 方法来判断两个元素是否相等的

注:基本类型可以直接去重,对于自定义类型需要重写hashcode和equals方法

public static void main(String[] args) {
    List<Integer> integers = Arrays.asList(1, 2, 3, 4, 4, 5, 5, 6, 7, 8, 2, 2, 2, 2);
    integers.stream().distinct().collect(Collectors.toList());
    
}
reduce

如果将所有数据归纳为一个数据,可以使用reduce

 String s = Stream.of("1", "2", "3", "4", "5", "6")
                // identity 是默认值
                .reduce("0",(x, y) -> {
                    System.out.println("x= "+x+" y= "+y);
                    return x +y;
                });
        System.out.println(s);
/**
输出结果:
    x= 0 y= 1
    x= 01 y= 2
    x= 012 y= 3
    x= 0123 y= 4
    x= 01234 y= 5
    x= 012345 y= 6
    0123456
*/
min/max

max,min可以寻找出流中最大或者最小的元素

List<String> maxLists = new ArrayList<>();
maxLists.add("a");
maxLists.add("b");
maxLists.add("c");
maxLists.add("d");
maxLists.add("e");
maxLists.add("f");
maxLists.add("hahaha");
int maxLength = maxLists.stream().mapToInt(s->s.length()).max().getAsInt();

System.out.println("字符串长度最长的长度为"+maxLength);
match

有的时候,我们只需要判断集合中是否全部满足条件,或者判断集合中是否有满足条件的元素,这时候就可以使用match方法:
allMatch:Stream 中全部元素符合传入的 predicate,返回 true
anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true

noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true

//  判断集合中没有有为‘c’的元素:
List<String> matchList = new ArrayList<>();
matchList.add("a");
matchList.add("a");
matchList.add("c");
matchList.add("d"); 
boolean isExits = matchList.stream().anyMatch(s -> s.equals("c"));

//判断集合中是否全不为空
List<String> matchList = new ArrayList<>();
matchList.add("a");
matchList.add("");
matchList.add("a");
matchList.add("c");
matchList.add("d");
boolean isNotEmpty = matchList.stream().noneMatch(s -> s.isEmpty());
collect

Stream流中提供了一个方法,可以把流中的数据收集到单列集合中:
<R, A> R collect(Collector<? super T, A, R> collector); 把流中的数据收集到单列集合中
返回值类型是R。R指定为什么类型,就是收集到什么类型的集合
参数Collector<? super T, A, R>中的R类型,决定把流中的元素收集到哪个集合中
参数Collector如何得到 ?,可以使用 java.util.stream.Collectors工具类中的静态方法:

  • public static Collector<T, ?, List> toList():转换为List集合
    tatic Collector<T, ?, Set> toSet() :转换为Set集合

    public class Tests {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("张三");
            list.add("张三四");
            list.add("王五");
            list.add("赵六");
            list.add("张三三");
            list.add("周八");
            // 需求:过滤出姓张的并且长度为3的元素
            Stream<String> stream1 = list.stream().filter(name -> name.startsWith("张")).filter(name -> name.length() == 3);
     
            // stream 收集到单列集合中
            List<String> list1 = stream1.collect(Collectors.toList());
            System.out.println(list1);
     
            // stream 收集到单列集合中
            Set<String> set1 = stream1.collect(Collectors.toSet());
            System.out.println(set1);
            
            // 将stream流中的元素转成Array数组对象
            Object[] arr = stream1.toArray();
            System.out.println(Arrays.toString(arr))
        }
    }

串行流/并行流

串行流:流默认使用的就是串行的,即按序执行

并行流:parallelStream是一个并行执行流,它通过默认的ForkJoinPool,提高多线程的任务速度。

获取并行流的两种方式:

//串行流转换为并行流
   Stream<String> parallel = Stream.of("1", "2", "3", "4", "5", "6")
           .parallel();
   //List接口直接获取并行流
   Stream<Object> parallelStream = new ArrayList<Object>().parallelStream();

注:并行流可能会有数据安全问题,应对业务逻辑加锁

5、Optional类

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

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

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

public final class Optional<T> extends Object

基本使用

Optional对象的创建方式

ofNullable:允许一个可能为空的对象

of:需要一个不为空的对象

这里需要自行判断,初始化的对象是否必然非空。

//1、通过of
    Optional<String> op1 = Optional.of("nihao");
    //of不支持null会报空指针异常
   // Optional<Object> op2 = Optional.of(null);

    //2、 通过ofNullable
    Optional<String> op3 = Optional.ofNullable("hello");
    Optional<Object> op4 = Optional.ofNullable(null);
    
    //3、通过entity
    Optional<Object> op5 = Optional.empty();

Optional常用方法

  • empty:可以创建一个空的Optional对象
  • get:直接获取Optional内部对象,但是不建议单独使用
  • isPresent:判断内部对象是否为空,返回一个布尔值
  • ifPresent:一样判断内部对象是否为空,如果不为空会执行lambda表达式
  • filter:通过一定条件过滤对象
  • flatMap:和map的区别为lambda入参的对象封装入了Optional
  • orElse:如果值为空,返回一个对象
  • orElseGet:如果值为空,执行一段lambda并返回一个对象
  • orElseThrow:如果值为空,抛出一个异常
public static void main(String[] args) {
    Optional<String> op1 = Optional.of("nihao");
    Optional<Object> op2 = Optional.empty();

    //1、get()方法获取值,有值返回值,无值抛出NoSuchElementException 异常
    System.out.println(op1.get());    //nihao
    System.out.println(op2.get());    //No value present

    //2、isPresent() 判断是否包含值,包含值返回true,不包含返回false
    //   ifPresent(T->{...}) 如果存在值就执行Lambda表达式
    System.out.println(op1.isPresent());  //true
    System.out.println(op2.isPresent());  //false

    //3、 orElse(T t) 如果调用对象包含值,就返回该值,否则返回t
    //   orElseGet(Supplier s) 如果调用对象包含值,就返回该值,否则返回Lambda表达式的返回值
    System.out.println(op1.orElse("hi"));   //nihao
    System.out.println(op2.orElse("123"));  //123
    
}

6、新时间日期API

java 8通过发布新的Date-Time API进一步加强对日期与时间的处理。在旧版的java中,日期时间APi存在诸多问题。java 8引入的新的一系列API,对时间日期处理提供了更好的支持,清楚的定义了时间日期的一些概念,比如说,瞬时时间(Instant),持续时间(duration),日期(date),时间(time),时区(time-zone)以及时间段(Period)。同时,借鉴了Joda库的一些优点,比如将人和机器对时间日期的理解区分开的。

新时间常用类

jdk8中新增加了一套全新的日期时间API,这套API设计合理,并且是线程安全的。API位于java.time包下:

  • LocalDate:表示日期,包含年月日,格式为2020-01-11。
  • LocalTime:表示时间,包含时分秒,格式为11:07:03.580。
  • LocalDateTime:表示日期和时间组合,包含年月日,时分秒,格式为2020-01-11T11:07:03.580
  • DateTimeFormatter:日期时间格式化类
  • Instant:时间戳,表示一个特定的时间瞬间
  • Duration:用于计算两个时间(LocalTime,时分秒)的距离
  • Period:用于计算2个日期(LocalDate,年月日)的距离
  • ZoneDateTime:包含时区的时间

基本使用

时间获取

//获取当前日期
LocalDate currDate=LocalDate.now();
//指定日期
LocalDate noeDay=LocalDate.of(2020, 1, 11);
//通过字符串指定日期
LocalDate towDay=LocalDate.parse("2020-01-11");

// 年
int year = currDate.getYear();
// 月
int month = currDate.getMonthValue();
// 一月的第几天
int day = currDate.getDayOfMonth();
// 一周的第几天
int week = currDate.getDayOfWeek().getValue();

时间操作

//   时间比较(LocalDate重写了equals方法,让日期的比较也变得简单了。)

System.out.println("比较两个日期是否相同:"+date1.equals(date2));  //true
//isBefore在之前
boolean isBefore=LocalDate.parse("2020-01-11").isBefore(LocalDate.parse("2020-01-10"));
//isAfter在之后
boolean isAfter=LocalDate.parse("2020-01-11").isAfter(LocalDate.parse("2020-01-10"));

// 日期加减
System.out.println("当前时间"+LocalDate.now());
System.out.println("当前时间加1天"+LocalDate.now().plusDays(1));
System.out.println("当前时间加1月"+LocalDate.now().plusMonths(1));
System.out.println("当前时间加1年"+LocalDate.now().plusYears(1));

//LocalDate的atTime()方法或LocalTime的atDate()方法将LocalDate或LocalTime合并成一个LocalDateTime。
//LocalDateTime与LocalDate和LocalTime之间可以相互转化
LocalDateTime datetime4=LocalDate.now().atTime(LocalTime.now());
LocalDateTime datetime5=LocalTime.now().atDate(LocalDate.now());

//ZonedDateTime-创建时区时间。用于处理带时区的日期和时间。ZoneId表示不同的时区。大约有40不同的时区。

Set<String> allZoneIds=ZoneId.getAvailableZoneIds();
//创建时区:
ZoneId zoneId=ZoneId.of("Asia/Shanghai");
//把LocalDateTime转换成特定的时区:
ZonedDateTime zonedDateTime=ZonedDateTime.of(LocalDateTime.now(), zoneId);
//获取当前时区
ZoneId z=ZoneId.systemDefault();
//获取日期时间:
ZonedDateTime dd = ZonedDateTime.now();
ZonedDateTime date1 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]");

日期格式化

java.time.format.DateTimeFormatter类下:

Java8对日期的格式化操作非常简单,首先看到上面的类大多都提供了parse方法,可以直接通过解析字符串得到对应的对象。

而日期和时间的格式化可通过LocalDateTime的format方法进行格式化。

LocalDateTime dateTime=LocalDateTime.now();
String str=dateTime.format(DateTimeFormatter.ISO_DATE_TIME);
System.out.println(str);
str = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(str);

可以使用DateTimeFormatter预置的格式,也可以通过DateTimeFormatter.ofPattern方法来指定格式。

计算时间差

可以使用以下类来计算日期时间差异:1.Period 2.Duration 3.ChronoUnit

//日期时间差
LocalDate today = LocalDate.now();
LocalDate birthDate = LocalDate.of(1993, Month.OCTOBER, 19);
Period p = Period.between(birthDate, today);   //  birthDate - today
System.out.printf("年龄 : %d 年 %d 月 %d 日", p.getYears(), p.getMonths(), p.getDays()); 

//时分秒时间差
Instant inst1 = Instant.now();
Instant inst2 = inst1.plus(Duration.ofSeconds(10));
System.out.println("Difference in milliseconds : " + Duration.between(inst1, inst2).toMillis()); 
System.out.println("Difference in seconds : " + Duration.between(inst1, inst2).getSeconds());

//ChronoUnit类可用于在单个时间单位内测量一段时间,例如天数或秒
LocalDate startDate = LocalDate.of(1993, Month.OCTOBER, 19);
System.out.println("开始时间  : " + startDate);
LocalDate endDate = LocalDate.of(2017, Month.JUNE, 16);
System.out.println("结束时间 : " + endDate);
long daysDiff = ChronoUnit.DAYS.between(startDate, endDate);
System.out.println("两天之间的差在天数   : " + daysDiff);//两天之间的差在天数   : 8641

文章作者: wumangeng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wumangeng !
赏
  目录