https://blog.csdn.net/qq_29411737/article/details/80835658

jdk1.7新特性

1、泛型实例的创建可以通过类型推断来简化,可以去掉后面new部分的泛型类型,只用<>就可以了。

2、并发工具增强: fork-join框架最大的增强,充分利用多核特性,将大问题分解成各个子问题,由多个cpu 可以同时 解决多个子问题最后合并结果,继承RecursiveTask,实现compute方法,然后调用fork计算,最后用join合并结果

3、try-with-resources语句是一种声明了一种或多种资源的try语句。资源是指在程序用完了之后必须要关闭的对象。try-with-resources语句保证了每个声明了的资源在语句结束的时候都会被关闭。任何实现了java.lang.AutoCloseable接口的对象,和实现了java .io .Closeable接口的对象,都可以当做资源使用
TWR语法详情

4、Catch多个异常:在Java 7中,catch代码块得到了升级,用以在单个catch块中处理多个异常。如果你要捕获多个异常并且它们包含相似的代码,使用这一特性将会减少代码重复度。下面用一个例子来理解。



catch(IOException | SQLException | Exception ex){
     logger.error(ex);
     throw new MyException(ex.getMessage());
}


jdk1.8新特性知识点:

1 jdk1.8对hashMap等map集合的优化

2 Lambda表达式

3 函数式接口

4 方法引用和构造器调用

5 Stream API

6 并行流和串行流

7 Optional容器
Java 8引入Optional类来防止空指针异常,Optional类最先是由Google的Guava项目引入的。Optional类实际上是个容器:它可以保存类型T的值或者保存null。使用Optional类我们就不用显式进行空指针检查了。

8 接口中的默认方法和静态方法

9 新时间日期API

10 定义可重复的注解
Java 5中使用注解有一个限制,即相同的注解在同一位置只能声明一次。Java 8引入重复注解,这样相同的注解在同一地方也可以声明多次重复注解机制本身需要用@Repeatable注解。Java 8在编译器层做了优化,相同注解会以集合的方式保存,因此底层的原理并没有变化。

11 扩展注解的支持
Java 8扩展了注解的上下文几乎可以为任何东西添加注解,包括局部变量泛型类父类与接口的实现连方法的异常也能添加注解。

12 jvm中的方法区变成了元数据区PermGen变成了Metaspace
13 更好的类型推测机制(不需要太多的强制类型转换了)
14 编译器优化:Java 8 将方法的参数名加入了字节码中,这样在运行时 通过反射 就能获取到参数名,只需要在编译时使用-parameters参数。

jdk1.8对hashMap等map集合的优化

hashMap数据结构的优化:
原来的hashMap采用的数据结构是哈希表(数组+链表),hashMap默认大小是16,一个0-15索引的数组,如何往里面存储元素,首先调用元素的hashcode
方法,计算出哈希码值,经过哈希算法算成数组的索引值,如果对应的索引处没有元素,直接存放,如果有对象在,那么比较它们的equals方法比较内容
如果内容一样,后一个value会将前一个value的值覆盖,如果不一样,在1.7的时候,后加的放在前面,形成一个链表,形成了碰撞,在某些情况下如果链表
无限下去,那么效率极低,碰撞是避免不了的

加载因子:0.75,数组扩容,达到总容量的75%,就进行扩容,但是无法避免碰撞的情况发生。

在1.8之后,在数组+链表+红黑树来实现hashmap,当碰撞的元素个数大于8时 & 总容量大于64,会有红黑树的引入。

除了添加之后,效率都比链表高,1.8之后链表新进元素加到末尾

ConcurrentHashMap (锁分段机制)concurrentLeveljdk1.8采用CAS算法(无锁算法,不再使用锁分段),数组+链表中也引入了红黑树的使用

Lambda表达式

lambda表达式本质上是一段匿名内部类也可以是一段可以传递的代码

先来体验一下lambda最直观的优点:简洁代码

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

  TreeSet<Integer> set = new TreeSet<>(cpt);

  System.out.println("=========================");

  //使用lambda表达式
  Comparator<Integer> cpt2 = (x,y) -> Integer.compare(x,y);
  TreeSet<Integer> set2 = new TreeSet<>(cpt2);

只需要一行代码,极大减少代码量!!

我们发现实际上循环过滤方法的核心只有if语句中的条件判断,其他均为模版代码,每次变更一下需求,都需要新增一个方法,然后复制黏贴,假设这个过滤方法有几百行,那么这样的做法难免笨拙了一点。如何进行优化呢?

方法1:使用lamabda表达式

定义一个MyPredicate接口

public interface MyPredicate <T> {
    boolean test(T t);
}

定义过滤方法:

public List<Product> filterProductByPredicate(List<Product> list,MyPredicate<Product> mp){
        List<Product> prods = new ArrayList<>();
        for (Product prod : list){
            if (mp.test(prod)){
                prods.add(prod);
            }
        }
        return prods;
    }

使用lambda表达式进行过滤

@Test
public void test4(){
      List<Product> products = filterProductByPredicate(proList, (p) -> p.getPrice() < 8000);
      for (Product pro : products){
          System.out.println(pro);
      }
  }

在jdk1.8中还有更加简便的操作 Stream API

方法2:使用Stream API

甚至不用定义过滤方法直接在集合上进行操作

// 使用jdk1.8中的Stream API进行集合的操作
@Test
public void test(){
    // 根据价格过滤
    proList.stream()
           .fliter((p) -> p.getPrice() <8000)
           .limit(2)
           .forEach(System.out::println);

    // 根据颜色过滤
    proList.stream()
           .fliter((p) -> "红色".equals(p.getColor()))
           .forEach(System.out::println);

    // 遍历输出商品名称
    proList.stream()
           .map(Product::getName)
           .forEach(System.out::println);
}

Lamabda表达式的语法总结: () -> ();

口诀:左右遇一省括号,左侧推断类型省

注:当一个接口中存在多个抽象方法时如果使用lambda表达式并不能智能匹配对应的抽象方法,因此引入了函数式接口的概念

函数式接口

函数式接口的提出是为了给Lambda表达式的使用提供更好的支持。

什么是函数式接口?

简单来说就是只定义了一个抽象方法的接口(Object类的public方法除外),就是函数式接口,并且还提供了注解@FunctionalInterface

常见的四大函数式接口

Consumer 《T》:消费型接口,有参无返回值

@Test
public void test(){
    changeStr("hello",(str) -> System.out.println(str));
}

/**
 *  Consumer<T> 消费型接口
 * @param str
 * @param con
 */
public void changeStr(String str, Consumer<String> con){
    con.accept(str);
}

Supplier 《T》:供给型接口,无参有返回值

@Test
public void test2(){
    String value = getValue(() -> "hello");
    System.out.println(value);
}

/**
 *  Supplier<T> 供给型接口
 * @param sup
 * @return
 */
public String getValue(Supplier<String> sup){
    return sup.get();
}

Function 《T,R》::函数式接口,有参有返回值

@Test
public void test3(){
    Long result = changeNum(100L, (x) -> x + 200L);
    System.out.println(result);
}

/**
 *  Function<T,R> 函数式接口
 * @param num
 * @param fun
 * @return
 */
public Long changeNum(Long num, Function<Long, Long> fun){
    return fun.apply(num);
}

Predicate《T》: 断言型接口,有参有返回值,返回值是boolean类型

public void test4(){
        boolean result = changeBoolean("hello", (str) -> str.length() > 5);
        System.out.println(result);
    }

    /**
     *  Predicate<T> 断言型接口
     * @param str
     * @param pre
     * @return
     */
    public boolean changeBoolean(String str, Predicate<String> pre){
        return pre.test(str);
    }

四大核心函数式接口基础上,还提供了诸如BiFunction、BinaryOperation、toIntFunction等扩展的函数式接口,都是在这四种函数式接口上扩展而来的,不做赘述。

总结:函数式接口的提出是为了让我们更加方便的使用lambda表达式不需要自己再手动创建一个函数式接口,直接拿来用就好了,贴

方法引用

若lambda体中的内容有方法已经实现了,那么可以使用“方法引用”也可以理解为方法引用是lambda表达式的另外一种表现形式,并且其语法比lambda表达式更加简单

(a) 方法引用

三种表现形式:
对象 : : 实例方法名
类 : : 静态方法名
类 : : 实例方法名 (lambda参数列表中第一个参数是实例方法的调用者,第二个参数是实例方法的参数时可用)

 public void test() {
        /**
        *注意:
        *   1.lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
        *   2.若lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method
        *
        */
        Consumer<Integer> con = (x) -> System.out.println(x);
        con.accept(100);

        // 方法引用-对象::实例方法
        Consumer<Integer> con2 = System.out::println;
        con2.accept(200);

        // 方法引用-类名::静态方法名
        BiFunction<Integer, Integer, Integer> biFun = (x, y) -> Integer.compare(x, y);
        BiFunction<Integer, Integer, Integer> biFun2 = Integer::compare;
        Integer result = biFun2.apply(100, 200);

        // 方法引用-类名::实例方法名
        BiFunction<String, String, Boolean> fun1 = (str1, str2) -> str1.equals(str2);
        BiFunction<String, String, Boolean> fun2 = String::equals;
        Boolean result2 = fun2.apply("hello", "world");
        System.out.println(result2);
    }

(b)构造器引用

格式:ClassName::new

public void test2() {

        // 构造方法引用  类名::new
        Supplier<Employee> sup = () -> new Employee();
        System.out.println(sup.get());
        Supplier<Employee> sup2 = Employee::new;
        System.out.println(sup2.get());

        // 构造方法引用 类名::new (带一个参数)
        Function<Integer, Employee> fun = (x) -> new Employee(x);
        Function<Integer, Employee> fun2 = Employee::new;
        System.out.println(fun2.apply(100));
 }

( c )数组引用

格式:Type[]::new

public void test(){
        // 数组引用
        Function<Integer, String[]> fun = (x) -> new String[x];
        Function<Integer, String[]> fun2 = String[]::new;
        String[] strArray = fun2.apply(10);
        Arrays.stream(strArray).forEach(System.out::println);
}

Stream API

Stream操作的三个步骤

★ 创建stream
★ 中间操作(过滤、map)
★ 终止操作

stream的创建:

// 1,校验通过Collection 系列集合提供的stream()或者paralleStream()
List<String> list = new ArrayList<>();
Strean<String> stream1 = list.stream();

// 2.通过Arrays的静态方法stream()获取数组流
String[] str = new String[10];
Stream<String> stream2 = Arrays.stream(str);

// 3.通过Stream类中的静态方法of
Stream<String> stream3 = Stream.of("aa","bb","cc");

// 4.创建无限流
// 迭代
Stream<Integer> stream4 = Stream.iterate(0,(x) -> x+2);

//生成
Stream.generate(() ->Math.random());

Stream的中间操作:

/**
   * 筛选 过滤  去重
   */
  emps.stream()
          .filter(e -> e.getAge() > 10)
          .limit(4)
          .skip(4)
          // 需要流中的元素重写hashCode和equals方法
          .distinct()
          .forEach(System.out::println);


  /**
   *  生成新的流 通过map映射
   */
  emps.stream()
          .map((e) -> e.getAge())
          .forEach(System.out::println);


  /**
   *  自然排序  定制排序
   */
  emps.stream()
          .sorted((e1 ,e2) -> {
              if (e1.getAge().equals(e2.getAge())){
                  return e1.getName().compareTo(e2.getName());
              } else{
                  return e1.getAge().compareTo(e2.getAge());
              }
          })
          .forEach(System.out::println);

Stream的终止操作:

 /**
         *      查找和匹配
         *          allMatch-检查是否匹配所有元素
         *          anyMatch-检查是否至少匹配一个元素
         *          noneMatch-检查是否没有匹配所有元素
         *          findFirst-返回第一个元素
         *          findAny-返回当前流中的任意元素
         *          count-返回流中元素的总个数
         *          max-返回流中最大值
         *          min-返回流中最小值
         */

        /**
         *  检查是否匹配元素
         */
        boolean b1 = emps.stream()
                .allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b1);

        boolean b2 = emps.stream()
                .anyMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b2);

        boolean b3 = emps.stream()
                .noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b3);

        Optional<Employee> opt = emps.stream()
                .findFirst();
        System.out.println(opt.get());

        // 并行流
        Optional<Employee> opt2 = emps.parallelStream()
                .findAny();
        System.out.println(opt2.get());

        long count = emps.stream()
                .count();
        System.out.println(count);

        Optional<Employee> max = emps.stream()
                .max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(max.get());

        Optional<Employee> min = emps.stream()
                .min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(min.get());

还有功能比较强大的两个终止操作 reduce和collect

reduce操作: reduce:(T identity,BinaryOperator)/reduce(BinaryOperator)-可以将流中元素反复结合起来,得到一个值

     /**
     *  reduce :规约操作
     */
    List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    Integer count2 = list.stream()
            .reduce(0, (x, y) -> x + y);
    System.out.println(count2);

    Optional<Double> sum = emps.stream()
            .map(Employee::getSalary)
            .reduce(Double::sum);
    System.out.println(sum);

collect操作: Collect-将流转换为其他形式接收一个Collection接口的实现,用于给Stream中元素做汇总的方法

    /**
     *  collect:收集操作
     */

    List<Integer> ageList = emps.stream()
            .map(Employee::getAge)
            .collect(Collectors.toList());
    ageList.stream().forEach(System.out::println);

并行流和串行流

jdk1.8新的stream包针对集合的操作也提供了并行操作流串行操作流。并行流就是把内容切割成多个数据块,并且使用多个线程分别处理每个数据块的内容。Stream api中声明可以通过parallel()sequential()方法在并行流串行流之间进行切换。

支持对数组进行并行处理,主要是parallelSort()方法,它可以在多核机器上 极大提高数组排序的速度

jdk1.8并行流使用的是fork/join框架进行并行操作。

ForkJoin框架(JDK1.7 的特性)
Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总

关键字:递归分合、分而治之。

采用 “工作窃取”模式(work-stealing):
当执行新的任务时,它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列偷一个并把它放在自己的队列中。相对于一般的线程池实现,fork/join框架的优势 体现在对其中包含的任务的处理方式上在一般的线程池中,如果一个线程正在执行的任务,由于某些原因无法继续运行,那么该线程会处于等待状态。而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行,那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行。这种方式减少了线程的等待时间提高了性能

/**
 * 要想使用Fork—Join,类必须继承
 * RecursiveAction(无返回值)
 * Or
 * RecursiveTask(有返回值)
*
*/
public class ForkJoin extends RecursiveTask<Long> {

    /**
     * 要想使用Fork—Join,类必须继承RecursiveAction(无返回值) 或者
     * RecursiveTask(有返回值)
     *
     * @author Wuyouxin
     */
    private static final long serialVersionUID = 23423422L;

    private long start;
    private long end;

    public ForkJoin() {
    }

    public ForkJoin(long start, long end) {
        this.start = start;
        this.end = end;
    }

    // 定义阙值
    private static final long THRESHOLD = 10000L;

    @Override
    protected Long compute() {
        if (end - start <= THRESHOLD) {
            long sum = 0;
            for (long i = start; i < end; i++) {
                sum += i;
            }
            return sum;
        } else {
            long middle = (end - start) / 2;
            ForkJoin left = new ForkJoin(start, middle);
            //拆分子任务,压入线程队列
            left.fork();
            ForkJoin right = new ForkJoin(middle + 1, end);
            right.fork();

            //合并并返回
            return left.join() + right.join();
        }
    }

    /**
     * 实现数的累加
     */
    @Test
    public void test1() {
        //开始时间
        Instant start = Instant.now();

        //这里需要一个线程池的支持
        ForkJoinPool pool = new ForkJoinPool();

        ForkJoinTask<Long> task = new ForkJoin(0L, 10000000000L);
        // 没有返回值     pool.execute();
        // 有返回值
        long sum = pool.invoke(task);

        //结束时间
        Instant end = Instant.now();
        System.out.println(Duration.between(start, end).getSeconds());
    }

    /**
     * java8 并行流 parallel()
     */
    @Test
    public void test2() {
        //开始时间
        Instant start = Instant.now();

        // 并行流计算    累加求和
        LongStream.rangeClosed(0, 10000000000L).parallel()
                .reduce(0, Long :: sum);

        //结束时间
        Instant end = Instant.now();
        System.out.println(Duration.between(start, end).getSeconds());
    }

    @Test
    public void test3(){
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        list.stream().forEach(System.out::print);

        list.parallelStream()
            .forEach(System.out::print);
    }

展示多线程的效果:

@Test
    public void test(){
        // 并行流 多个线程执行
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        numbers.parallelStream()
                .forEach(System.out::print);

        //
        System.out.println("=========================");
        numbers.stream()
                     .sequential()
                     .forEach(System.out::print);
    }

Optional容器

使用Optional容器可以快速的定位NPE,并且在一定程度上可以减少对参数非空检验的代码量

/**
     *      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
     *
     *      总结:Optional.of(null)  会直接报NPE
     */

Optional<Employee> op = Optional.of(new Employee("zhansan", 11, 12.32, Employee.Status.BUSY));
        System.out.println(op.get());

        // NPE
        Optional<Employee> op2 = Optional.of(null);
        System.out.println(op2);
@Test
    public void test2(){
        Optional<Object> op = Optional.empty();
        System.out.println(op);

        // No value present
        System.out.println(op.get());
    }
@Test
    public void test3(){
        Optional<Employee> op = Optional.ofNullable(new Employee("lisi", 33, 131.42, Employee.Status.FREE));
        System.out.println(op.get());

        Optional<Object> op2 = Optional.ofNullable(null);
        System.out.println(op2);
       // System.out.println(op2.get());
    }
    @Test
    public void test5(){
        Optional<Employee> op1 = Optional.ofNullable(new Employee("张三", 11, 11.33, Employee.Status.VOCATION));
        System.out.println(op1.orElse(new Employee()));
        System.out.println(op1.orElse(null));
    }

    @Test
    public void test6(){
        Optional<Employee> op1 = Optional.of(new Employee("田七", 11, 12.31, Employee.Status.BUSY));
        op1 = Optional.empty();
        Employee employee = op1.orElseGet(() -> new Employee());
        System.out.println(employee);
    }

    @Test
    public void test7(){
        Optional<Employee> op1 = Optional.of(new Employee("田七", 11, 12.31, Employee.Status.BUSY));
        System.out.println(op1.map( (e) -> e.getSalary()).get());
    }

接口:定义默认实现方法和静态方法

在接口中可以使用defaultstatic关键字来修饰接口中定义的普通方法

public interface Interface {
    default  String getName(){
        return "zhangsan";
    }

    static String getName2(){
        return "zhangsan";
    }
}

在JDK1.8中很多接口会新增方法,为了保证1.8向下兼容,1.7版本中的接口实现类不用每个都重新实现新添加的接口方法,引入了default默认实现static的用法是直接用接口名去调方法即可。当一个类继承父类实现接口时,若后两者方法名相同,则优先继承父类中的同名方法,即“类优先”;如果实现两个同名方法的接口,则要求实现类必须手动声明默认实现哪个接口中的方法
一个接口有默认方法,考虑这样的情况,一个类实现了多个接口,且这些接口有相同的默认方法,以下实例说明了这种情况的解决方法:

以下实例转自:
https://blog.csdn.net/yitian_66/article/details/81010434#t17

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

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

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

public class Car implements vehicle, fourWheeler {

    @Override
    public void print() {
        System.out.println("我是一辆四轮汽车!");
    }
}

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

public class Car implements vehicle, fourWheeler {
    @Override
    public void print() {
        vehicle.super.print();
    }
}

新的日期API:LocalDate | LocalTime | LocalDateTime

新的日期API都是不可变的,更适用于多线程的使用环境中。

@Test
public void test(){
    // 从默认时区的系统时钟获取当前的日期时间。不用考虑时区差
    LocalDateTime date = LocalDateTime.now();
    //2018-07-15T14:22:39.759
    System.out.println(date);

    System.out.println(date.getYear());
    System.out.println(date.getMonthValue());
    System.out.println(date.getDayOfMonth());
    System.out.println(date.getHour());
    System.out.println(date.getMinute());
    System.out.println(date.getSecond());
    System.out.println(date.getNano());

    // 手动创建一个LocalDateTime实例
    LocalDateTime date2 = LocalDateTime.of(2017, 12, 17, 9, 31, 31, 31);
    System.out.println(date2);
    // 进行加操作,得到新的日期实例
    LocalDateTime date3 = date2.plusDays(12);
    System.out.println(date3);
    // 进行减操作,得到新的日期实例
    LocalDateTime date4 = date3.minusYears(2);
    System.out.println(date4);
}

@Test
public void test2(){
    // 时间戳  1970年1月1日00:00:00 到某一个时间点的毫秒值
    // 默认获取UTC时区
    Instant ins = Instant.now();
    System.out.println(ins);

    System.out.println(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli());
    System.out.println(System.currentTimeMillis());

    System.out.println(Instant.now().toEpochMilli());
    System.out.println(Instant.now().atOffset(ZoneOffset.ofHours(8)).toInstant().toEpochMilli());
}

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

    Instant ins1 = Instant.now();

    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Instant ins2 = Instant.now();
    Duration dura = Duration.between(ins1, ins2);
    System.out.println(dura);
    System.out.println(dura.toMillis());

    System.out.println("======================");
    LocalTime localTime = LocalTime.now();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    LocalTime localTime2 = LocalTime.now();
    Duration du2 = Duration.between(localTime, localTime2);
    System.out.println(du2);
    System.out.println(du2.toMillis());
}

@Test
    public void test4(){
        LocalDate localDate =LocalDate.now();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        LocalDate localDate2 = LocalDate.of(2016,12,12);
        Period pe = Period.between(localDate, localDate2);
        System.out.println(pe);
    }

@Test
public void test5(){
    // temperalAdjust 时间校验器
    // 例如获取下周日  下一个工作日
    LocalDateTime ldt1 = LocalDateTime.now();
    System.out.println(ldt1);

    // 获取一年中的第一天
    LocalDateTime ldt2 = ldt1.withDayOfYear(1);
    System.out.println(ldt2);
    // 获取一个月中的第一天
    LocalDateTime ldt3 = ldt1.withDayOfMonth(1);
    System.out.println(ldt3);

    LocalDateTime ldt4 = ldt1.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
    System.out.println(ldt4);

    // 获取下一个工作日
    LocalDateTime ldt5 = ldt1.with((t) -> {
        LocalDateTime ldt6 = (LocalDateTime)t;
        DayOfWeek dayOfWeek = ldt6.getDayOfWeek();
        if (DayOfWeek.FRIDAY.equals(dayOfWeek)){
            return ldt6.plusDays(3);
        }
        else if (DayOfWeek.SATURDAY.equals(dayOfWeek)){
            return ldt6.plusDays(2);
        }
        else {
            return ldt6.plusDays(1);
        }
    });
    System.out.println(ldt5);
}

@Test
public void test6(){
    // DateTimeFormatter: 格式化时间/日期
    // 自定义格式
    LocalDateTime ldt = LocalDateTime.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
    String strDate1 = ldt.format(formatter);
    String strDate = formatter.format(ldt);
    System.out.println(strDate);
    System.out.println(strDate1);

    // 使用api提供的格式
    DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE;
    LocalDateTime ldt2 = LocalDateTime.now();
    String strDate3 = dtf.format(ldt2);
    System.out.println(strDate3);

    // 解析字符串to时间
    DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    LocalDateTime time = LocalDateTime.now();
    String localTime = df.format(time);
    LocalDateTime ldt4 = LocalDateTime.parse("2017-09-28 17:07:05",df);
    System.out.println("LocalDateTime转成String类型的时间:"+localTime);
    System.out.println("String类型的时间转成LocalDateTime:"+ldt4);
}

// ZoneTime  ZoneDate       ZoneDateTime
@Test
public void test7(){
    LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
    System.out.println(now);

    LocalDateTime now2 = LocalDateTime.now();
    ZonedDateTime zdt = now2.atZone(ZoneId.of("Asia/Shanghai"));
    System.out.println(zdt);

    Set<String> set = ZoneId.getAvailableZoneIds();
    set.stream().forEach(System.out::println);
}

补充:

  • 表示日期的 LocalDate
  • 表示时间的 LocalTime
  • 表示日期时间的 LocalDateTime

新的日期API的几个优点:

  • 之前使用的java.util.Date月份从0开始,我们一般会+1使用,很不方便,java.time.LocalDate月份和星期都改成了enum

  • java.util.DateSimpleDateFormat 都不是线程安全的,而LocalDate和LocalTime和最基本的String一样是不变类型不但线程安全而且不能修改

  • java.util.Date是一个“万能接口”,它包含日期、时间,还有毫秒数更加明确需求取舍

  • 新接口更好用的原因是考虑到了日期时间的操作,经常发生往前推或往后推几天的情况。用java.util.Date配合Calendar要写好多代码,而且一般的开发人员还不一定能写对。

LocalDate

public static void localDateTest() {

        //获取当前日期,只含年月日 固定格式 yyyy-MM-dd    2018-05-04
        LocalDate today = LocalDate.now();

        // 根据年月日取日期,5月就是5,
        LocalDate oldDate = LocalDate.of(2018, 5, 1);

        // 根据字符串取:默认格式yyyy-MM-dd,02不能写成2
        LocalDate yesteday = LocalDate.parse("2018-05-03");

        // 如果不是闰年 传入29号也会报错
        LocalDate.parse("2018-02-29");
    }

LocalDate常用转化

/**
 * 日期转换常用,第一天或者最后一天...
 */
public static void localDateTransferTest(){
    //2018-05-04
    LocalDate today = LocalDate.now();
    // 取本月第1天: 2018-05-01
    LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth());
    // 取本月第2天:2018-05-02
    LocalDate secondDayOfThisMonth = today.withDayOfMonth(2);
    // 取本月最后一天,再也不用计算是28,29,30还是31: 2018-05-31
    LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());
    // 取下一天:2018-06-01
    LocalDate firstDayOf2015 = lastDayOfThisMonth.plusDays(1);
    // 取2018年10月第一个周三 so easy?:  2018-10-03
    LocalDate thirdMondayOf2018 = LocalDate.parse("2018-10-01").with(TemporalAdjusters.firstInMonth(DayOfWeek.WEDNESDAY));
}

LocalTime

 public static void localTimeTest(){
        //16:25:46.448(纳秒值)
        LocalTime todayTimeWithMillisTime = LocalTime.now();
        //16:28:48 不带纳秒值
        LocalTime todayTimeWithNoMillisTime = LocalTime.now().withNano(0);
        LocalTime time1 = LocalTime.parse("23:59:59");
    }

LocalDateTime

public static void localDateTimeTest(){
        //转化为时间戳  毫秒值
        long time1 = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
        long time2 = System.currentTimeMillis();

        //时间戳转化为localdatetime
        DateTimeFormatter df= DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss.SSS");

        System.out.println(df.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(time1),ZoneId.of("Asia/Shanghai"))));
    }
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐