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 !
  目录